Había una vez un modesto controlador de vista VCYellow . Y no tenía ni una imagen, ni texto, ni siquiera una pequeña lógica de negocios. Vivió una vida ordinaria de controlador de vista.
Su compañero controlador de vista VCMain a veces lo presentaba al 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) }
Y VCYellow, a su vez, se escondía con la ayuda de un solo botón "X", del cual, por cierto, estaba muy orgulloso:
class VCYellow: UIViewController { ... @IBAction func onBtnCloseTapped(_ sender: Any) { self.dismiss(animated: true, completion: nil) }
Y no se veía tan mal, sino aburrido y mundano:

Pero nuestro héroe tuvo un sueño para aprender a mostrar y esconderse en la belleza. Sí, para que pueda cambiar esta belleza más adelante en las vacaciones o simplemente en honor a un buen humor.

Pasaron los años ... y el sueño habría seguido siendo un sueño si no hubiera sido por VCYellow aprender sobre la magia llamada:
UIViewControllerTransitioningDelegate
Y el poder de esta magia radica en el hecho de que hace posible deslizar el animador apropiado para mostrar y ocultar el controlador de vista. Justo lo que soñaba nuestro controlador.
Leyó en pergaminos antiguos cómo usar el hechizo y comenzó a prepararse.
Escribí una cuna con el hechizo en sí para no olvidar:
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) } }
En él, pintó cuidadosamente que para el programa debe usar el animador AnimatorPresent y al cerrar AnimatorDismiss .
Bueno, como ayuda para ambos animadores, se decidió transferir el marco del botón principal de VCMain
Y luego él mismo estaba mentalmente sintonizado. Porque sin la actitud correcta, como sabes, ninguna magia funciona:
override func viewDidLoad() { super.viewDidLoad() self.modalPresentationStyle = .custom self.transitioningDelegate = self }
Le pidió a su amigo VCMain que se presentara para comprobar cómo funciona la magia y ... no funcionó de ninguna manera ...
Resultó que AnimatorPresent y AnimatorDismiss no aparecen por sí mismos.
Era demasiado tarde para parar, y nuestro héroe decidió crear los animadores necesarios. Busqué en la sección necesaria de pergaminos antiguos y descubrí que dos cosas son suficientes para crear un animador.
Primero, debe establecer el tiempo asignado para la animación:
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.3 }
y en segundo lugar indicar la animación en sí:
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) }) }
- Extraiga el controlador de vista presentado (en nuestro caso VCYellow) y tome una foto de él. Se necesita una foto para simplificar la animación.
- Obtenga una vista sobre qué brujería animada tendrá lugar. Llamémoslo contexto.
- Enganche la vista del controlador final en el contexto y escóndelo. Mostrar
- se decidió después de que terminó la animación.
- Prepara una foto para la animación. Reduzca al tamaño inicial y agregue el contexto.
- Divida la foto en pantalla completa, animando así el proceso de presentación.
- Una vez finalizada la animación, muestre la vista real del controlador final,
- deshazte de la foto e informa que la acción ha terminado.
Como resultado, había un animador para mostrar:
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) }) } }
Y después de eso no fue difícil escribir un animador para esconderse, que hace casi lo mismo, pero viceversa:
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) }) } }
Habiendo terminado todos los toques finales , VCYellow nuevamente le pidió a su amigo VCMain que se presentara y ¡ he aquí !

¡La magia funcionó! ¡El sueño de VCYellow se hizo realidad! ¡Ahora puede mostrar y esconderse como quiera y nada limitará su imaginación!
Un proyecto de ejemplo se puede descargar aquí.
El artículo que usé para inspirarme está aquí.