iOS 7 utilise des transitions interactives personnalisées seulement une partie du time

J'ai donc un UINavigationController, avec le controller A comme controller racine.

Quand je veux pousser le controller B au-dessus, je veux utiliser une transition animée personnalisée et une transition interactive personnalisée. Cela fonctionne bien.

Quand je veux pousser le controller C au dessus, je veux revenir aux transitions push / pop par défaut qui viennent avec UINavigationController. Pour ce faire, je reviens nul pour

navigationController:animationControllerForOperation:fromViewController:toViewController: 

Cependant, si vous revenez à zéro, alors

 navigationController:interactionControllerForAnimationController: 

ne sera jamais appelé et la transition interactive pop "pan from left edge" par défaut ne fonctionne pas.

Existe-t-il un moyen de renvoyer le controller d'animation push / pop par défaut et le controller d'interaction? (Existe-t-il des implémentations concrètes d' id<UIViewControllerAnimatedTransitioning> et d' id<UIViewControllerInteractiveTransitioning> ?)

Ou d'une autre manière?

Vous devez définir le délégué interactivePopGestureRecognizer de NavigationController sur self, puis gérer son comportement dans -gestureRecognizerShouldBegin:

C'est-à-dire, lorsque vous voulez que le mouvement pop embedded se triggers, vous devez renvoyer YES à partir de cette méthode. Il en va de même pour vos gestes personnalisés – vous devez déterminer quel reconnaisseur vous avez affaire.

 - (void)setup { self.interactiveGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleTransitionGesture:)]; self.interactiveGestureRecognizer.delegate = self; [self.navigationController.view addGestureRecognizer:self.interactiveGestureRecognizer]; self.navigationController.interactivePopGestureRecognizer.delegate = self; } - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { // Don't handle gestures if navigation controller is still animating a transition if ([self.navigationController.transitionCoordinator isAnimated]) return NO; if (self.navigationController.viewControllers.count < 2) return NO; UIViewController *fromVC = self.navigationController.viewControllers[self.navigationController.viewControllers.count-1]; UIViewController *toVC = self.navigationController.viewControllers[self.navigationController.viewControllers.count-2]; if ([fromVC isKindOfClass:[ViewControllerB class]] && [toVC isKindOfClass:[ViewControllerA class]]) { if (gestureRecognizer == self.interactiveGestureRecognizer) return YES; } else if (gestureRecognizer == self.navigationController.interactivePopGestureRecognizer) { return YES; } return NO; } 

Vous pouvez consulter un exemple de projet pour votre scénario. Les transitions entre les controllers de vue A et B sont animées de façon personnalisée, avec un mouvement pop B-> A personnalisé. Les transitions entre les controllers de vue B et C sont par défaut, avec le mouvement pop du controller de navigation embedded.

J'espère que cela t'aides!

Vous devrez définir le délégué à chaque fois avant de le présenter – dans prepareForSeque: par exemple. Si vous voulez la transition personnalisée, définissez-la sur Self. Si vous voulez que la transition par défaut (comme la transition pop par défaut) le place à zéro.

Définir le délégué avant / après chaque transition est une solution de contournement valide, mais si vous implémentez les autres UINavigationControllerDelegate de UINavigationControllerDelegate et que vous devez les conserver, vous pouvez avoir 2 objects délégué comme suggéré par Ziconic ou jouer avec respondsToSelector de NSObject:. Dans votre délégué de navigation, vous pouvez implémenter:

 - (BOOL)respondsToSelector:(SEL)aSelector { if (aSelector == @selector(navigationController:animationControllerForOperation:fromViewController:toViewController:) || aSelector == @selector(navigationController:interactionControllerForAnimationController:)) { return self.interactivePushTransitionEnabled; } return [super respondsToSelector:aSelector]; } 

Vous devez ensuite vous assurer de mettre à jour interactivePushTransitionEnabled si nécessaire. Dans votre exemple, vous devez définir la propriété sur YES uniquement lorsque le controller A est affiché.

Il n'y a plus qu'une chose à faire: forcer UINavigationController à réévaluer les methods implémentées par son délégué. Cela peut être facilement fait en faisant quelque chose comme ceci:

 navigationController.delegate = nil; navigationController.delegate = self; // or whatever object you use as the delegate 

essayez de définir (self.)navigationController.delegate sur nil (ou renvoyez-le à une valeur précédente) après avoir effectué votre transition personnalisée.

Dans votre outil de reconnaissance de mouvements, réglez le délégué (sur vous-même) et returnnez vos transitions animées et interactives. lorsque vous renvoyez la transition interactive, assurez-vous de désactiver à nouveau le délégué pour que tout le rest continue à utiliser les transitions par défaut.

J'ai un exemple de travail sur github .

 - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController { // Unset the delegate so that all other types of transitions (back, normal animated push not initiated by a gesture) get the default behavior. self.navigationController.delegate = nil; if (self.edgePanGestureRecognizer.state == UIGestureRecognizerStateBegan) { self.percentDrivenInteractiveTransition = [UIPercentDrivenInteractiveTransition new]; } else { self.percentDrivenInteractiveTransition = nil; } return self.percentDrivenInteractiveTransition; }