Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 476
Florian Avatar asked May 26 '14 21:05

Florian


2 Answers

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.

like image 163
danh Avatar answered Sep 18 '22 20:09

danh


Updated for Swift

  1. Class Definition

    class LeftRightAnimationController: NSObject, UIViewControllerAnimatedTransitioning
    
  2. Set Navigation Delegate

     class NavigationControllerDelegate: NSObject, UINavigationControllerDelegate {
    
         //Custom animation controller for settings table vc
         let leftRightAnimationController = LeftRightAnimationController()
    
         func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
             if toVC.isKind(of: SettingsTableViewController.self){
                 self.leftRightAnimationController.originFrame = fromVC.view.frame
                 return self.leftRightAnimationController
             }
             return nil
         }
    
     }
    
  3. Animator Duration

     func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
         return 0.75
     }`
    
  4. Implement Left to Right Slide

     func animateTransition(using transitionContext:    UIViewControllerContextTransitioning) {
         guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) else { return }
         guard let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
    
         let fromVCFrame = fromVC.view.frame
    
         let width = toVC.view.frame.size.width
         transitionContext.containerView.addSubview(toVC.view)
         toVC.view.frame = fromVCFrame.offsetBy(dx: width, dy: 0)
    
         UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
             fromVC.view.frame = fromVCFrame.offsetBy(dx: -width, dy: 0)
             toVC.view.frame = fromVCFrame
         }) { (finished) in
             fromVC.view.frame = fromVCFrame
             transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
         }
     }
    
like image 35
joshbillions Avatar answered Sep 20 '22 20:09

joshbillions