Il était une fois un modeste contrôleur de vue VCYellow . Et il n'avait ni image, ni texte, ni même une toute petite logique commerciale. Il a vécu une vie ordinaire de contrôleur de vue.
Son collègue contrôleur de vue VCMain l'a parfois présenté au monde:
class VCMain: UIViewController { ... @IBAction func onBtnTapMeTapped(_ sender: Any) { let vcYellow = self.storyboard!.instantiateViewController(withIdentifier: "VCYellow") as! VCYellow self.present(vcYellow, animated: true, completion: nil) }
Et VCYellow, à son tour, se cachait à l'aide d'un seul bouton «X», dont il était d'ailleurs très fier:
class VCYellow: UIViewController { ... @IBAction func onBtnCloseTapped(_ sender: Any) { self.dismiss(animated: true, completion: nil) }
Et ça n'avait pas l'air si mal, mais ennuyeux et banal:

Mais notre héros a rêvé d'apprendre à se montrer et à se cacher dans la beauté. Oui, pour pouvoir changer cette beauté plus tard dans les vacances ou juste en l'honneur de la bonne humeur.

Des années ont passé ... et donc le rêve serait resté un rêve si VCYellow n'avait pas appris la magie:
UIViewControllerTransitioningDelegate
Et la puissance de cette magie réside dans le fait qu'elle permet de glisser l'animateur approprié pour afficher et masquer le contrôleur de vue. Exactement ce dont rêvait notre contrôleur.
Il a lu dans des rouleaux anciens comment utiliser le sort et a commencé à se préparer.
J'ai écrit une crèche avec le sort lui-même pour ne pas oublier:
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) } }
Dans ce document, il a soigneusement peint cela pour le spectacle, vous devez utiliser l'animateur AnimatorPresent et lors de la fermeture d' AnimatorDismiss .
Eh bien, pour aider les deux animateurs, il a été décidé de transférer le cadre du bouton principal de VCMain
Et puis lui-même a été réglé mentalement. Parce que sans la bonne attitude, comme vous le savez, aucune magie ne fonctionne:
override func viewDidLoad() { super.viewDidLoad() self.modalPresentationStyle = .custom self.transitioningDelegate = self }
Il a demandé à son ami VCMain de se présenter pour vérifier le fonctionnement de la magie et ... ça n'a pas fonctionné ...
Il s'est avéré que AnimatorPresent et AnimatorDismiss n'apparaissent pas d'eux-mêmes.
Il était trop tard pour s'arrêter et notre héros a décidé de créer les animateurs nécessaires. J'ai fouillé dans la section nécessaire des anciens parchemins et j'ai découvert que deux choses suffisaient pour créer un animateur.
Tout d'abord, vous devez définir le temps alloué à l'animation:
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.3 }
et ensuite indiquer l'animation elle-même:
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) }) }
- Retirez le contrôleur de vue présenté (dans notre cas VCYellow) et prenez-en une photo. Une photo est nécessaire pour simplifier l'animation.
- Obtenez une vue sur laquelle la sorcellerie animée aura lieu. Appelons-le contexte.
- Accrochez la vue du contrôleur final sur le contexte et masquez-la. Afficher
- il a été décidé après la fin de l'animation.
- Préparez une photo pour l'animation. Réduisez à la taille initiale et lancez le contexte.
- Divisez la photo en plein écran, animant ainsi le processus de présentation.
- Une fois l'animation terminée, affichez la vue réelle du contrôleur final,
- se débarrasser de la photo et signaler que l'action est terminée.
En conséquence, il y avait un tel animateur pour l'affichage:
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) }) } }
Et après cela, il n'a pas été difficile d'écrire un animateur pour se cacher, ce qui fait à peu près la même chose, mais 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) }) } }
Après avoir terminé toutes les touches finales , VCYellow a de nouveau demandé à son ami VCMain de se présenter et voilà !

La magie a fonctionné! Le rêve de VCYellow s'est réalisé! Maintenant, il peut se montrer et se cacher comme il veut et rien ne limitera son imagination!
Un exemple de projet peut être téléchargé ici.
L'article que j'ai utilisé pour l'inspiration est ici.