我创建了一个与UIPageViewController
类似的自定义容器控制器,这样我就可以实现一些自定义转换&数据源逻辑。我尝试模仿新客户视图控制器转换API在iOS 7中的工作方式,并且除了在转换 取消时的视图外观回调时出现一些令人恼火的怪癖 ...
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
而不管转换是否被取消,那么当取消转换时,我的视图回调看起来像这样:
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)
但是,下次我开始转换时,我收到没有视图外观回调。下一组视图外观回调仅在beginAppearanceTransition:animated:
后跟endApperanceTransition
调用后到达。值得注意的是,我不收到经常报告的“对ViewController 开始/结束外观转换的不平衡调用”控制台警告。
在WWDC 2013 Session 218 (Custom Transitions Using View Controllers)布鲁斯·尼洛(Bruce Nilo)对他的同事们说了viewWillAppear:
& viewWillDisappear:
确实应该被称为viewMightAppear:
& viewMightDisappear:
(请参阅从42:00开始的该会话部分)。鉴于我们现在处于可取消的交互式手势领域,我们似乎需要cancelAppearanceTransition
(或endAppearanceTransition:(BOOL)finished
)方法来定制容器。
答案 0 :(得分:17)
因此,通常采用SO的方式,你花费数小时无效地解决问题,然后在你发布问题后,你就会找到答案......
我查看了UINavigationController
内置interactivePopGestureRecognizer
的外观回调,看看取消转换时会发生什么,看来我对外观方法应该< / em>被称为错误。
对于成功转换到的视图控制器,会收到以下回调(正如您所期望的那样):
transition
finished
viewWillAppear: ----------> viewDidAppear:
但是对于不成功的视图控制器转换(即转换被取消),视图控制器会收到以下回调:
transition immediately
cancelled followed by
viewWillAppear: ----------> viewWillDisappear: ----------> viewDidDisappear:
所以我们得到了更多那些狡猾的视图外观语义;我不确定视图如何消失如果还没有出现!哦,好吧......我认为它比一个说明会出现,然后什么都没发生的观点要好一些。
因此,返回我的CustomTransitionContext
的{{1}}代码,解决方案如下所示:
transitionCompletionHandler
所以现在外观回调看起来像这样:
...
// 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];
}
}
....
此外,后续转换现在会按预期触发回调(因为我们通过调用oldVC viewWillDisappear:
newVC viewWillAppear:
// ... some time later transition is cancelled ...
newVC viewWillDisappear:
newVC viewDidDisappear:
oldVC viewWillAppear:
oldVC viewDidAppear:
来关闭所有转换)。