Era uma vez um controlador de exibição modesto VCYellow . E ele não tinha uma imagem, nem texto, nem mesmo uma pequena lógica de negócios. Ele viveu uma vida comum de controlador de visão.
Seu colega controlador de exibição VCMain às vezes o apresentava ao mundo:
class VCMain: UIViewController { ... @IBAction func onBtnTapMeTapped(_ sender: Any) { let vcYellow = self.storyboard!.instantiateViewController(withIdentifier: "VCYellow") as! VCYellow self.present(vcYellow, animated: true, completion: nil) }
E o VCYellow, por sua vez, estava escondido com a ajuda de um único botão "X", do qual ele, a propósito, estava muito orgulhoso de:
class VCYellow: UIViewController { ... @IBAction func onBtnCloseTapped(_ sender: Any) { self.dismiss(animated: true, completion: nil) }
E não parecia tão ruim, mas chato e mundano:

Mas nosso herói teve um sonho de aprender a mostrar e se esconder na beleza. Sim, para que você possa mudar essa beleza mais tarde nas férias ou apenas em homenagem ao bom humor.

Os anos se passaram ... e, portanto, o sonho continuaria sendo um sonho, se não fosse a VCYellow aprender sobre mágica chamada:
UIViewControllerTransitioningDelegate
E o poder dessa mágica está no fato de que permite deslizar o animador apropriado para mostrar e ocultar o controlador de exibição. Exatamente o que nosso controlador sonhava.
Ele leu em pergaminhos antigos como usar o feitiço e começou a se preparar.
Escrevi um berço com o próprio feitiço para não esquecer:
extension VCYellow: UIViewControllerTransitioningDelegate { func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return AnimatorPresent(startFrame: self.startFrame) } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return AnimatorDismiss(endFrame: self.startFrame) } }
Nele, ele pintou cuidadosamente que, para o programa, você precisa usar o animador AnimatorPresent e ao fechar o AnimatorDismiss .
Bem, como uma ajuda para ambos os animadores, decidiu-se transferir o quadro principal de botões do VCMain
E então ele próprio estava mentalmente afinado. Porque sem a atitude correta, como você sabe, nenhuma mágica funciona:
override func viewDidLoad() { super.viewDidLoad() self.modalPresentationStyle = .custom self.transitioningDelegate = self }
Ele pediu ao amigo VCMain que se apresentasse para verificar como a mágica funciona e ... não funcionou de maneira alguma ...
Descobriu-se que AnimatorPresent e AnimatorDismiss não aparecem por si mesmos.
Era tarde demais para parar, e nosso herói decidiu criar os animadores necessários. Percorri a seção necessária dos pergaminhos antigos e descobri que duas coisas são suficientes para criar um animador.
Primeiro, você precisa definir o tempo alocado para a animação:
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.3 }
e, em segundo lugar, indique a própria animação:
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { //1 guard let vcTo = transitionContext.viewController(forKey: .to), let snapshot = vcTo.view.snapshotView(afterScreenUpdates: true) else { return } //2 let vContainer = transitionContext.containerView //3 vcTo.view.isHidden = true vContainer.addSubview(vcTo.view) //4 snapshot.frame = self.startFrame vContainer.addSubview(snapshot) UIView.animate(withDuration: 0.3, animations: { //5 snapshot.frame = (transitionContext.finalFrame(for: vcTo)) }, completion: { success in //6 vcTo.view.isHidden = false snapshot.removeFromSuperview() transitionContext.completeTransition(true) }) }
- Puxe o controlador de visualização apresentado (no nosso caso, VCYellow) e tire uma foto dele. É necessária uma foto para simplificar a animação.
- Veja qual bruxaria animada ocorrerá. Vamos chamar de contexto.
- Conecte a visualização do controlador final no contexto e oculte-o. Mostrar
- foi decidido após o término da animação.
- Prepare uma foto para animação. Reduza para o tamanho inicial e jogue no contexto.
- Divida a foto em tela cheia, animando o processo de apresentação.
- Depois que a animação terminar, mostre a visão real do controlador final,
- Livre-se da foto e relate que a ação terminou.
Como resultado, havia um animador para exibição:
import UIKit class AnimatorPresent: NSObject, UIViewControllerAnimatedTransitioning { let startFrame: CGRect init(startFrame: CGRect) { self.startFrame = startFrame } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.3 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let vcTo = transitionContext.viewController(forKey: .to), let snapshot = vcTo.view.snapshotView(afterScreenUpdates: true) else { return } let vContainer = transitionContext.containerView vcTo.view.isHidden = true vContainer.addSubview(vcTo.view) snapshot.frame = self.startFrame vContainer.addSubview(snapshot) UIView.animate(withDuration: 0.3, animations: { snapshot.frame = (transitionContext.finalFrame(for: vcTo)) }, completion: { success in vcTo.view.isHidden = false snapshot.removeFromSuperview() transitionContext.completeTransition(true) }) } }
E depois disso, não foi difícil escrever um animador para se esconder, o que faz o mesmo, mas vice-versa:
import UIKit class AnimatorDismiss: NSObject, UIViewControllerAnimatedTransitioning { let endFrame: CGRect init(endFrame: CGRect) { self.endFrame = endFrame } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.3 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let vcTo = transitionContext.viewController(forKey: .to), let vcFrom = transitionContext.viewController(forKey: .from), let snapshot = vcFrom.view.snapshotView(afterScreenUpdates: true) else { return } let vContainer = transitionContext.containerView vContainer.addSubview(vcTo.view) vContainer.addSubview(snapshot) vcFrom.view.isHidden = true UIView.animate(withDuration: 0.3, animations: { snapshot.frame = self.endFrame }, completion: { success in transitionContext.completeTransition(true) }) } }
Depois de terminar todos os retoques finais , VCYellow novamente pediu a seu amigo VCMain que se apresentasse e pronto !

A mágica funcionou! O sonho de VCYellow se tornou realidade! Agora ele pode mostrar e se esconder como quiser e nada limitará sua imaginação!
Um exemplo de projeto pode ser baixado aqui.
O artigo que usei para inspiração está aqui.