如何"取消"自定义容器控制器转换的视图外观转换

Stu*_*art 16 transitions uiviewcontroller ios ios7 custom-transition

我创建了一个自定义容器控制器,其工作方式类似于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];
    }
}
Run Code Online (Sandbox Code Playgroud)

因此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.
Run Code Online (Sandbox Code Playgroud)

如果我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)
Run Code Online (Sandbox Code Playgroud)

但是,下次我开始转换时,我没有收到任何视图外观回调.下一组视图外观回调仅在beginAppearanceTransition:animated:跟随endApperanceTransition调用之后到达.值得注意的是,我没有收到经常报告的" 对ViewController开始/结束外观转换的不平衡调用 "控制台警告.

WWDC 2013届218(自定义转换使用视图控制器)布鲁斯·尼罗使有关他的同事,而相关的笑话告诉他viewWillAppear::viewWillDisappear:真的应该被称为viewMightAppear:&viewMightDisappear: (见该届会议的部分开始于42:00) .鉴于我们现在处于可取消的交互式手势领域,似乎我们需要一个cancelAppearanceTransition(或endAppearanceTransition:(BOOL)finished)方法来定制容器.

有人知道我做错了什么吗?或者是可取消的,自定义容器中的自定义转换尚未正确支持?

Stu*_*art 18

因此,通常采用SO的方式,你花费数小时毫无工作地处理问题,然后在你发布问题之后,你找到答案......

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

对于成功转换到的视图控制器,将收到以下回调(如您所料):

                 transition
                  finished
viewWillAppear:  ---------->  viewDidAppear:
Run Code Online (Sandbox Code Playgroud)

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

                 transition                       immediately
                 cancelled                        followed by
viewWillAppear:  ---------->  viewWillDisappear:  ---------->  viewDidDisappear:
Run Code Online (Sandbox Code Playgroud)

所以我们得到了更多那些狡猾的视图外观语义; 如果一个视图尚未出现,我不确定它是如何消失的!哦,好吧......我认为它比看起来出现的观点略胜一筹,然后什么都没发生.

所以,回到我CustomTransitionContexttransitionCompletionHandler代码,解决方案看起来像这样:

...

// 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];
    }
}

....
Run Code Online (Sandbox Code Playgroud)

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

oldVC viewWillDisappear:
newVC viewWillAppear:
// ... some time later transition is cancelled ...
newVC viewWillDisappear:
newVC viewDidDisappear:
oldVC viewWillAppear:
oldVC viewDidAppear:
Run Code Online (Sandbox Code Playgroud)

此外,后续转换现在按预期触发回调(因为我们通过调用关闭所有转换endAppearanceTransition).