How to 'cancel' view appearance transitions for custom container controller transitions

后端 未结 1 1471
难免孤独
难免孤独 2021-01-31 10:49

I have created a custom container controller that works similarly to a UIPageViewController so that I could implement some custom transitions & data source logi

1条回答
  •  傲寒
    傲寒 (楼主)
    2021-01-31 11:30

    So as is often the way with SO, you spend hours fruitlessly working on a problem, and then right after you post the question, you find the answer...

    I looked at the appearance callbacks of UINavigationController's built-in interactivePopGestureRecognizer to see what happens when the cancelling the transition, and it appears that my assumption about what appearance methods should be called was wrong.

    For a view controller that is successfully transitioned to, the following callbacks are received (as you'd expect):

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

    But for a view controller transition that is unsuccessful (i.e. the transition was cancelled), the view controller receives the following callbacks:

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

    So we get a bit more of those dodgy view appearance semantics; I'm not sure how a view can disappear if it hasn't yet appeared! Oh well... I suppose it's slightly better than a view saying it will appear, and then nothing happening.

    So, returning to my CustomTransitionContext's transitionCompletionHandler code, the solution looks like this:

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

    So now the appearance callbacks look like this:

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

    Also, subsequent transitions now trigger the callbacks as expected (since we're closing off all transitions with calls to endAppearanceTransition).

    0 讨论(0)
提交回复
热议问题