Animation de transition personnalisée du controller de navigation

J'ai suivi quelques tutoriels pour créer une animation personnalisée lors de la transition d'une vue à l'autre.

Mon projet de test utilisant le segue personnalisé à partir d' ici fonctionne bien, mais quelqu'un m'a dit que je ne suis plus encouragé à faire de l'animation personnalisée dans une section personnalisée, et je devrais utiliser UIViewControllerAnimatedTransitioning .

J'ai suivi plusieurs tutoriels qui utilisent ce protocole, mais tous concernent la présentation modale (par exemple ce tutoriel ).

Ce que j'essaie de faire est une séquence de push dans un tree de controller de navigation, mais quand j'essaye de faire la même chose avec un show (push), ça ne marche plus.

S'il vous plaît dites-moi la bonne façon de faire une transition personnalisée d'animation d'une vue à l'autre dans un controller de navigation.

Et est-ce que de toute façon je peux utiliser une méthode pour toutes les animations en transition? Ce serait gênant si un jour je veux faire la même animation mais que je doive dupliquer le code deux fois pour travailler sur la transition modale vs controller.

    Pour effectuer une transition personnalisée avec le controller de navigation ( UINavigationController ), vous devez:

    • Définissez votre controller de vue pour qu'il se conforme au protocole UINavigationControllerDelegate . Par exemple, vous pouvez avoir une extension de class privée dans le file .m de votre controller de .m qui spécifie la conformité à ce protocole:

       @interface ViewController () <UINavigationControllerDelegate> @end 
    • Assurez-vous de spécifier votre controller de vue en tant que délégué de votre controller de navigation:

       - (void)viewDidLoad { [super viewDidLoad]; self.navigationController.delegate = self; } 
    • Implémentez animationControllerForOperation dans votre controller de vue:

       - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController*)fromVC toViewController:(UIViewController*)toVC { if (operation == UINavigationControllerOperationPush) return [[PushAnimator alloc] init]; if (operation == UINavigationControllerOperationPop) return [[PopAnimator alloc] init]; return nil; } 
    • Mettre en place des animateurs pour les animations push et pop, par exemple:

       @interface PushAnimator : NSObject <UIViewControllerAnimatedTransitioning> @end @interface PopAnimator : NSObject <UIViewControllerAnimatedTransitioning> @end @implementation PushAnimator - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return 0.5; } - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; [[transitionContext containerView] addSubview:toViewController.view]; toViewController.view.alpha = 0.0; [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ toViewController.view.alpha = 1.0; } completion:^(BOOL finished) { [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; }]; } @end @implementation PopAnimator - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return 0.5; } - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; [[transitionContext containerView] insertSubview:toViewController.view belowSubview:fromViewController.view]; [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ fromViewController.view.alpha = 0.0; } completion:^(BOOL finished) { [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; }]; } @end 

      Cela aplanit la transition, mais vous devriez vous sentir libre de personnaliser l'animation comme bon vous semble.

    • Si vous voulez gérer des gestes interactifs (par exemple, quelque chose comme le balayage natif de gauche à droite pour apparaître), vous devez implémenter un controller d'interaction:

      • Définissez une propriété pour un controller d'interaction (un object conforme à UIViewControllerInteractiveTransitioning ):

         @property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactionController; 

        Cette UIPercentDrivenInteractiveTransition est un bel object qui effectue la lourde tâche de mettre à jour votre animation personnalisée en fonction de la finalité du geste.

      • Ajoutez un outil de reconnaissance de gestes à votre vue. Ici, je suis juste en train d'implémenter la reconnaissance de gestes gauche pour simuler un pop:

         UIScreenEdgePanGestureRecognizer *edge = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeFromLeftEdge:)]; edge.edges = UIRectEdgeLeft; [view addGestureRecognizer:edge]; 
      • Implémentez le gestionnaire de reconnaissance de gestes:

         /** Handle swipe from left edge * * This is the "action" selector that is called when a left screen edge gesture recognizer starts. * * This will instantiate a UIPercentDrivenInteractiveTransition when the gesture starts, * update it as the gesture is "changed", and will finish and release it when the gesture * ends. * * @param gesture The screen edge pan gesture recognizer. */ - (void)handleSwipeFromLeftEdge:(UIScreenEdgePanGestureRecognizer *)gesture { CGPoint translate = [gesture translationInView:gesture.view]; CGFloat percent = translate.x / gesture.view.bounds.size.width; if (gesture.state == UIGestureRecognizerStateBegan) { self.interactionController = [[UIPercentDrivenInteractiveTransition alloc] init]; [self popViewControllerAnimated:TRUE]; } else if (gesture.state == UIGestureRecognizerStateChanged) { [self.interactionController updateInteractiveTransition:percent]; } else if (gesture.state == UIGestureRecognizerStateEnded) { CGPoint velocity = [gesture velocityInView:gesture.view]; if (percent > 0.5 || velocity.x > 0) { [self.interactionController finishInteractiveTransition]; } else { [self.interactionController cancelInteractiveTransition]; } self.interactionController = nil; } } 
      • Dans votre délégué de controller de navigation, vous devez également implémenter la méthode de délégation interactionControllerForAnimationController

         - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController { return self.interactionController; } 

    Si vous google "tutoriel de transition personnalisé UINavigationController" et vous obtiendrez de nombreux hits. Ou consultez la video WWDC 2013 Custom Transitions .

    Vous pouvez append le code suivant avant addSubview

      toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController]; 

    D'une autre question custom-transition-pour-pousser-animation-avec-navigationcontroller-on-ios-9

    De la documentation d'Apple pour finalFrameForViewController:

    Renvoie le rectangle d'image de fin pour la vue du controller de vue spécifié.

    Le rectangle renvoyé par cette méthode représente la taille de la vue correspondante à la fin de la transition. Pour la vue couverte pendant la présentation, la valeur renvoyée par cette méthode peut être CGRectZero mais il peut également s'agir d'un rectangle d'image valide.

    En utilisant les réponses parfaites de Rob et Q i, voici le code Swift simplifié, en utilisant la même animation de fondu pour .push et .pop:

     extension YourViewController: UINavigationControllerDelegate { func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { //INFO: use UINavigationControllerOperation.push or UINavigationControllerOperation.pop to detect the 'direction' of the navigation class FadeAnimation: NSObject, UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.5 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) if let vc = toViewController { transitionContext.finalFrame(for: vc) transitionContext.containerView.addSubview(vc.view) vc.view.alpha = 0.0 UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: { vc.view.alpha = 1.0 }, completion: { finished in transitionContext.completeTransition(!transitionContext.transitionWasCancelled) }) } else { NSLog("Oops! Something went wrong! 'ToView' controller is nill") } } } return FadeAnimation() } } 

    N'oubliez pas de définir le délégué dans la méthode viewDidLoad () de YourViewController:

     override func viewDidLoad() { //... self.navigationController?.delegate = self //... }