La historia de un controlador de vista que quería lucir bien

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) }) } 

  1. 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.
  2. Obtenga una vista sobre qué brujería animada tendrá lugar. Llamémoslo contexto.
  3. Enganche la vista del controlador final en el contexto y escóndelo. Mostrar
  4. se decidió después de que terminó la animación.
  5. Prepara una foto para la animación. Reduzca al tamaño inicial y agregue el contexto.
  6. Divida la foto en pantalla completa, animando así el proceso de presentación.
  7. Una vez finalizada la animación, muestre la vista real del controlador final,
  8. 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í.

Source: https://habr.com/ru/post/es424853/


All Articles