iOS 7 change pushviewcontroller animation direction
Asked Answered
D

7

6

When i push a viewcontroller the animation is always from right to left. I want to change that animation to the inverse left to right. I have tried this code but doesnt work. The animation is still from right to left. How can i achive this simply and correctly? Need help please. Thanks in advance.

//NOT WORKING
myViewController *mController = [self.storyboard instantiateViewControllerWithIdentifier:@"myViewController"];

CATransition *transition = [CATransition animation];
transition.duration = 0.3;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;

[self.view.layer addAnimation:transition forKey:nil];

[self.navigationController pushViewController:mController animated:YES];
Dorena answered 20/10, 2014 at 12:27 Comment(1)
Try doing this : [self.view.window.layer addAnimation:transition forKey:nil]; instead of : [self.view.layer addAnimation:transition forKey:nil];Annadiana
I
9

You are actually doing it right, but as far as I understand from your code, you are not overriding the method:

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated

So, you need to inherit from UINavigationController and override the above method.

Here's how I do it (push + pop):

Push:

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
    UIView *theWindow = self.view ;
    if( animated ) {
        CATransition *animation = [CATransition animation];
        [animation setDuration:0.45f];
        [animation setType:kCATransitionPush];
        [animation setSubtype:kCATransitionFromLeft];
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
        [[theWindow layer] addAnimation:animation forKey:@""];
    }

    //make sure we pass the super "animated:NO" or we will get both our
    //animation and the super's animation
    [super pushViewController:viewController animated:NO];

    [self swapButtonsForViewController:viewController];

}

Pop :

- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
    UIView *theWindow = self.view ;
    if( animated ) {
        CATransition *animation = [CATransition animation];
        [animation setDuration:0.45f];
        [animation setType:kCATransitionPush];
        [animation setSubtype:kCATransitionFromRight];
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
        [[theWindow layer] addAnimation:animation forKey:@""];
    }
    return [super popViewControllerAnimated:NO];
}
Inflect answered 20/10, 2014 at 13:24 Comment(0)
T
11

Since this question mentions iOS 7 I'm quite surprised the answer doesn't mention the Animated Transitions API introduced in iOS 7.

If you want to get straight into GitHub the objc.io guys have a great post with a linked project here

Take a look at the documentation and you will see the following:

@availability(iOS, introduced=7.0)
optional func navigationController(navigationController: UINavigationController, 
animationControllerForOperation operation: UINavigationControllerOperation, 
fromViewController fromVC: UIViewController,
 toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?

Or in Objective-C

- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                   animationControllerForOperation:(UINavigationControllerOperation)operation
                                                fromViewController:(UIViewController *)fromVC
                                                  toViewController:(UIViewController *)toVC  NS_AVAILABLE_IOS(7_0);

What this means is that from iOS 7 you can control how the navigation controller implements its animations for pushes and pops.

All that is needed is for you to set the delegate of the navigation controller and vend the correct animator object when appropriate. So lets get started:

1. Set the UINavigationControllerDelegate

I typically like to have an App-Wide Navigation Coordinator that manages all my transitions etc. So ideally you want some object that will be alive for the duration of the NavigationController. So we might have a coordinator that looks like this:

class NavigationCoordinationController: NSObject {
    private(set) var navigationController:UINavigationController        

    required init(navigationController: UINavigationController) {
        self.navigationController = navigationController
        super.init()
        navigationController.delegate = self
    }
}

I typically create this in the App Delegate so it can be referenced anywhere. You could also set the delegate on a per view controller basis, creating a custom UINavigationController subclass, or wherever you see fit.

2. Create A Custom Animator

Now we need an object that conforms to the UIViewControllerAnimatedTransitioning protocol. This object will be called upon to apply the animation whenever an animated transition is needed. This example is a simple animation that fades and expands the fromView in the transition so its relevant for a Push.

class CustomPushExpansionTransitioner: NSObject, UIViewControllerAnimatedTransitioning {
    func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
        return 1
    }

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        let fromView: UIView = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
        let toView: UIView = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
        let container = transitionContext.containerView()
        container.addSubview(toView)
        container.bringSubviewToFront(fromView)

        toView.frame = container.convertRect(fromView.bounds, fromView: fromView)

        UIView.animateWithDuration(transitionDuration(transitionContext),
            animations: { () -> Void in
                fromView.alpha = 0
                fromView.transform = CGAffineTransformConcat(fromView.transform, CGAffineTransformMakeScale(1.3, 1.3))
        }) { (complete) -> Void in
            transitionContext.completeTransition(true)
        }
    }
}

3. Vend The Animator Where Necessary

Now we need to implement the UINavigationController delegate and vend our custom animator when needed.

extension NavigationCoordinationController: UINavigationControllerDelegate {
    func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        switch operation {
        case .Push:
            return LaunchToHomeTransitioner()
        case .Pop:
            break
        case .None:
            break
        }
        return nil
    }
}

And there you go, you now have total control over your UINavigationController transitions

Tag answered 5/5, 2015 at 15:3 Comment(4)
You create this in the appDelegate with a required init having a UINavigationController object. But the AppDelegate itself doesn't have a UINavigationController object. Do you just declare the instance, and then initialise it per-VC and set the individual View Controller's UINavigationController object?Percuss
@cocotutch you create the navigation controller in the app delegate with a root view controller, then instantiate the coordination controller... Alternatively you could subclass the nav controller and become its delegateTag
I see. I ended up declaring the variable and passing in the UIViewController navigation controller object across to it in the App Delegate on a per view controller basis. Because of the storyboard. It works anyhow.. Thanks!Percuss
Thanks. Its take some time to find how to use Animate Transition with pushViewController.Goggleeyed
I
9

You are actually doing it right, but as far as I understand from your code, you are not overriding the method:

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated

So, you need to inherit from UINavigationController and override the above method.

Here's how I do it (push + pop):

Push:

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
    UIView *theWindow = self.view ;
    if( animated ) {
        CATransition *animation = [CATransition animation];
        [animation setDuration:0.45f];
        [animation setType:kCATransitionPush];
        [animation setSubtype:kCATransitionFromLeft];
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
        [[theWindow layer] addAnimation:animation forKey:@""];
    }

    //make sure we pass the super "animated:NO" or we will get both our
    //animation and the super's animation
    [super pushViewController:viewController animated:NO];

    [self swapButtonsForViewController:viewController];

}

Pop :

- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
    UIView *theWindow = self.view ;
    if( animated ) {
        CATransition *animation = [CATransition animation];
        [animation setDuration:0.45f];
        [animation setType:kCATransitionPush];
        [animation setSubtype:kCATransitionFromRight];
        [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
        [[theWindow layer] addAnimation:animation forKey:@""];
    }
    return [super popViewControllerAnimated:NO];
}
Inflect answered 20/10, 2014 at 13:24 Comment(0)
T
4
NextViewController *next = [[NextViewController alloc] initWithNibName:@"NextViewController" bundle:nil];

[UIView transitionWithView:self.navigationController.view 
              duration:0.75 
               options:UIViewAnimationOptionTransitionFlipFromRight 
            animations:^{
             [self.navigationController pushViewController:next animated:NO];
            } 
            completion:nil];

Try Above code

Treasurer answered 27/8, 2015 at 11:17 Comment(1)
Need slide from left, not flipThekla
D
2

You can try this as well.

CATransition *transition = [CATransition animation];

transition.duration = 0.5;
transition.type = kCATransitionMoveIn;
transition.subtype = kCATransitionFromLeft;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

[self.navigationController.view.layer addAnimation:transition forKey:kCATransition];

DrawingResultsViewController *dr = [DrawingResultsViewController createAndShowProfile:YES];

[self.navigationController pushViewController:dr animated:YES];
Disinterest answered 14/7, 2015 at 10:49 Comment(0)
C
0

If some one is looking for the same answer in Xamarin.iOS (based on gran33 answer) :

public override void PushViewController(UIViewController viewController, bool animated)
{
    if (animated)
    {
        CATransition animation = new CATransition()
        {
            Duration = 0.5,
            Type = CAAnimation.TransitionPush,
            Subtype = CAAnimation.TransitionFromTop,
            TimingFunction = AMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut)
        };
        View.Layer.AddAnimation(animation, CALayer.Transition);

        base.PushViewController(viewController, false);
    }
}

public override UIViewController PopViewController(bool animated)
{
    if (animated)
    {
        CATransition animation = new CATransition()
        {
            Duration = 0.5,
            Type = CAAnimation.TransitionPush,
            Subtype = CAAnimation.TransitionFromBottom,
            TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut)
            };
            View.Layer.AddAnimation(animation, CALayer.Transition);

            return base.PopViewController(false);
        }
    return base.PopViewController(animated);
}
Carlcarla answered 27/7, 2017 at 10:0 Comment(0)
H
0

Assuming myViewController is your view controller, pass custom transition to the nav controller layer.

CATransition *transition = [CATransition animation];

transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;

[self.navigationController.view.layer addAnimation:transition forKey:@"pushViewController"]; 

[self.navigationController pushViewController:myViewController animated:NO];
Hickson answered 19/3, 2018 at 10:13 Comment(2)
For RTL languages, e.g., Arabic, iOS will automatically do that when the iOS language is changed from the iPhone/iPad Settings.Hickson
This works for me, but it looks a bit jenky compared to the normal push, I get some weird black background in the transition that isn't there on the normal push.Arvy
K
0

For Swift 4.0+

1 - Create a extension:

extension UINavigationController {
    func customPopView(_ viewController: UIViewController) {
        let transition: CATransition = CATransition()
        transition.duration = 0.3
        transition.type = CATransitionType.push
        transition.subtype = CATransitionSubtype.fromLeft
        view.layer.add(transition, forKey: nil)
        pushViewController(viewController, animated: false)
    }
}

2 - Call:

self.navigationController?.customPopView(yourViewController)
Kaleighkalends answered 6/6, 2019 at 11:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.