Interactive transitions between view controllers?

后端 未结 3 1249
借酒劲吻你
借酒劲吻你 2021-02-06 16:34

With the UIView animation API and view controller containment the current Cocoa Touch stack is very well suited for automatic transitions between view controllers.<

3条回答
  •  难免孤独
    2021-02-06 17:23

    This is the API I have arrived at. There are three components to it – the regular view controller that wants to create a transition to another one, a custom container view controller, and a transition class. The transition class looks like this:

    @interface TZInteractiveTransition : NSObject
    
    @property(strong) UIView *fromView;
    @property(strong) UIView *toView;
    
    // Usually 0–1 where 0 = just fromView visible and 1 = just toView visible
    @property(assign, nonatomic) CGFloat phase;
    // YES when the transition is taken far enough to perform the controller switch
    @property(assign, readonly, getter = isCommitted) BOOL committed;
    
    - (void) prepareToRun;
    - (void) cleanup;
    
    @end
    

    From this abstract class I derive the concrete transitions for pushing, rotating etc. Most work is done in the container controller (simplified a bit):

    @interface TZTransitionController : UIViewController
    
    @property(strong, readonly) TZInteractiveTransition *transition;
    
    - (void) startPushingViewController: (TZViewController*) controller withTransition: (TZInteractiveTransition*) transition;
    - (void) startPoppingViewControllerWithTransition: (TZInteractiveTransition*) transition;
    
    // This method finishes the transition either to phase = 1 (if committed),
    // or to 0 (if cancelled). I use my own helper animation class to step
    // through the phase values with a nice easing curve.
    - (void) endTransitionWithCompletion: (dispatch_block_t) completion;
    
    @end
    

    To make things a bit more clear, this is how the transition starts:

    - (void) startPushingViewController: (TZViewController*) controller withTransition: (TZInteractiveTransition*) transition
    {
        NSParameterAssert(controller != nil);
        NSParameterAssert([controller parentViewController] == nil);
    
        // 1. Add the new controller as a child using the containment API.
        // 2. Add the new controller’s view to [self view].
        // 3. Setup the transition:    
        [self setTransition:transition];
        [_transition setFromView:[_currentViewController view]];
        [_transition setToView:[controller view]];
        [_transition prepareToRun];
        [_transition setPhase:0];
    }
    

    The TZViewController is just a simple UIViewController subclass that holds a pointer to the transition controller (very much like the navigationController property). I use a custom gesture recognizer similar to UIPanGestureRecognizer to drive the transition, this is how gesture callback code in the view controller looks:

    - (void) handleForwardPanGesture: (TZPanGestureRecognizer*) gesture
    {
        TZTransitionController *transitionController = [self transitionController];
        switch ([gesture state]) {
            case UIGestureRecognizerStateBegan:
                [transitionController
                    startPushingViewController:/* build next view controller */
                    withTransition:[TZCarouselTransition fromRight]];
                break;
            case UIGestureRecognizerStateChanged: {
                CGPoint translation = [gesture translationInView:[self view]];
                CGFloat phase = fabsf(translation.x)/CGRectGetWidth([[self view] bounds]);
                [[transitionController transition] setPhase:phase];
                break;
            }
            case UIGestureRecognizerStateEnded: {
                [transitionController endTransitionWithCompletion:NULL];
                break;
            }
            default:
                break;
        }
    }
    

    I’m happy with the result – it’s fairly straightforward, uses no hacks, it’s easy to extend with new transitions and the code in the view controllers is reasonably short & simple. My only gripe is that I have to use a custom container controller, so I’m not sure how that plays with the standard containers and modal controllers.

提交回复
热议问题