Search code examples
iosuinavigationcontrollersegue

Seamless animation "from left to right" when pushing view controller?


I have an app with a root view controller and two additional view controllers. For design reasons, those are supposed to appear to be either to the left or to the right of the root VC. This spatial relationship should be represented in the way they are presented to the user when they appear on screen. That is, through the push-animation.

When pushing a new view controller, it enters the screen from the right so that works fine or one of my two VC's. For the other, I have created a custom segue based on this example that will make the VC appear from left to right.

This works but the animation itself is not seamless. It doesn't look as if the view is push onto the root VC from the left - it looks as if there's a black background that is visible before the view appears.

I was wondering whether anyone would know how to get an animation that's a perfectly mirrored version of the default "push" (i.e. from right to left) animation?


Solution

  • Check out this article. It does just what you want, except for the transition itself. In a nutshell:

    1) Create an object to perform the animation

    @interface Animator : NSObject <UIViewControllerAnimatedTransitioning>
    @end
    

    2) Become your navigation controller's delegate, and answer an instance of that animator on this nav controller delegate method:

    // in your vc.m
    @interface ViewController () <UINavigationControllerDelegate>  // add this
    
    // in view did load
    self.navigationController.delegate = self;
    
    // implement this to pass your animator as the thing to be in charge of the transition animation
    - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
    animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController*)fromVC toViewController:(UIViewController*)toVC {
    
        if (operation == UINavigationControllerOperationPush) {
            return [[Animator alloc] init];
        }
        return nil;
    }
    

    3) In your animator, answer a duration:

    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        return 0.25;
    }
    

    4) All of that is from the article. The only thing left to do is implement the left to right slide. (I changed the article's code to do your left to right slide):

    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    
        CGRect fromVCFrame = fromViewController.view.frame;
    
        CGFloat width = toViewController.view.frame.size.width;
        [[transitionContext containerView] addSubview:toViewController.view];
        toViewController.view.frame = CGRectOffset(fromVCFrame, -width, 0);
    
        [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
            fromViewController.view.frame = CGRectOffset(fromVCFrame, width, 0);
            toViewController.view.frame = fromVCFrame;
        } completion:^(BOOL finished) {
            fromViewController.view.frame = fromVCFrame;
            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        }];
    }
    

    I tried all this out in a little project and it works pretty slick.