如何为自定义容器控制器转换“取消”视图外观转换

我创build了一个类似于UIPageViewController的自定义容器控制器,以便我可以实现一些自定义转换和数据源逻辑。 我试图模仿新的客户视图控制器转换API在iOS 7中的工作方式,除了在取消转换时使用视图外观callback时出现的一些恼人的怪癖时,它运行良好。

也就是说,在进行转换的时候,究竟应该在什么时候开始出现转换beginAppearanceTransition:animated:endAppearanceTransition被调用?


我的自定义容器类有这样的代码:

 - (BOOL)shouldAutomaticallyForwardAppearanceMethods { return NO; // Since the automatic callbacks are wrong for our custom transition. } - (void)startTransition:(CustomTransitionContext *)context { // Get reference to the view controllers involved in the transition. UIViewController *oldVC = [context viewControllerForKey:UITransitionContextFromViewController]; UIViewController *newVC = [context UITransitionContextToViewController]; // Prepare parent/child relationship changes. [oldVC willMoveToParentViewController:nil]; [self addChildViewController:newVC]; // Begin view appearance transitions. [oldVC beginAppearanceTransition:NO animated:[context isAnimated]]; [newVC beginAppearanceTransition:YES animated:[context isAnimated]]; // Register a completion handler to run when the context's completeTransition: method is called. __weak CustomContainerController *weakSelf = self; context.transitionCompletionHandler = ^(BOOL complete) { // End appearance transitions here? [oldVC endAppearanceTransition]; [newVC endAppearanceTransition]; if (complete) { // Or only if the transition isn't cancelled; here? [oldVC endAppearanceTransition]; [newVC endAppearanceTransition]; [oldVC removeFromParentViewController]; [newVC didMoveToParentViewController:weakSelf]; } else { [newVC removeFromParentViewController]; [oldVC didMoveToParentViewController]; } } if ([context isInteractive] && [self.transitionController conformsToProtocol:@protocol(UIViewControllerInteractiveTransitioning)]) { // Start the interactive transition. [self.transitionController startInteractiveTransition:context]; } else if ([context isAnimated] && [self.transitionController conformsToProtocol:@protocol(UIViewControllerAnimatedTransitioning)]) { // Start the animated transition. [self.transitionController animateTransition:context]; } else { // Just complete the transition. [context completeTransition:YES]; } } 

所以如果我打电话endAppearanceTransition不pipe过渡是否被取消,那么当过渡被取消时,我的callback看起来像这样:

 oldVC viewWillDisappear: // Fine newVC viewWillAppear: // Fine // ... some time later transition is cancelled ... oldVC viewDidDisappear: // Wrong! This view controller's view is staying. newVC viewDidAppear: // Wrong! The appearance of this view controllers view was cancelled. 

如果我只是在转换成功的时候调用endAppearanceTransition ,事情看起来会好一些:

 oldVC viewWillDisappear: // Fine newVC viewWillAppear: // Fine // ... some time later transition is cancelled ... // ... silence. (which is correct - neither view actually appeared or disappeared, // and I can undo side effects in viewWill(Dis)Appear using the // transitionCoordinator object) 

但是,接下来我开始转换,我没有收到任何视图callback 。 下一组视图外观callback仅在beginAppearanceTransition:animated:后面是endApperanceTransition调用。 值得注意的是,我没有收到经常报告的“ 不平衡调用ViewController的开始/结束外观转换 ”控制台警告。

在WWDC 2013 Session 218(使用视图控制器的自定义转换) Bruce Nilo对他的同事们讲了一个相当有趣的笑话,告诉他这个viewWillAppear:viewWillDisappear:真的应该叫做viewMightAppear:viewMightDisappear: 参见42节开始的部分:00) 。 鉴于我们现在处于可取消互动手势领域,似乎我们需要一个cancelAppearanceTransition (或者endAppearanceTransition:(BOOL)finished )方法来定制容器。

有没有人知道我在做什么错了? 或者是可以取消的,自定义容器中的自定义转换只是不被正确支持?

所以,通常情况下,你会花费数小时的时间来解决问题,然后在问题发布后,你会find答案。

我查看了UINavigationController内置的interactivePopGestureRecognizer的外观callbackUINavigationController ,看看在取消转换时会发生什么,看起来我应该调用什么外观方法的假设是错误的。

对于成功转换到的视图控制器,会收到以下callback(如您所期望的):

  transition finished viewWillAppear: ----------> viewDidAppear: 

但是对于不成功的视图控制器转换(即转换被取消),视图控制器会收到以下callback:

  transition immediately cancelled followed by viewWillAppear: ----------> viewWillDisappear: ----------> viewDidDisappear: 

所以我们可以看到更多的那些狡猾的视图外观语义。 我不确定一个视图如果还没有出现,它是如何消失的 ! 噢,我想这比稍微说出来的观点要好一点,然后什么也没有发生。

所以,返回到我的CustomTransitionContexttransitionCompletionHandler代码,解决scheme如下所示:

 ... // Register a completion handler to run when the context's completeTransition: method is called. __weak CustomContainerController *weakSelf = self; __weak CustomTransitionContext *weakContext = context; context.transitionCompletionHandler = ^(BOOL complete) { if (complete) { // End the appearance transitions we began earlier. [oldVC endAppearanceTransition]; [newVC endAppearanceTransition]; [oldVC removeFromParentViewController]; [newVC didMoveToParentViewController:weakSelf]; } else { // Before ending each appearance transition, begin an // appearance transition in the opposite direction. [newVC beginAppearanceTransition:NO animated:[weakContext isAnimated]]; [newVC endAppearanceTransition]; [oldVC beginAppearanceTransition:YES animated:[weakContext isAnimated]]; [oldVC endAppearanceTransition]; [newVC removeFromParentViewController]; [oldVC didMoveToParentViewController]; } } .... 

所以现在外观callback看起来像这样:

 oldVC viewWillDisappear: newVC viewWillAppear: // ... some time later transition is cancelled ... newVC viewWillDisappear: newVC viewDidDisappear: oldVC viewWillAppear: oldVC viewDidAppear: 

另外,随后的转换现在会按预期触发callback(因为我们正在closures所有转换,并调用endAppearanceTransition )。