Restore pre-iOS7 UINavigationController pushViewController animation
Solution 1
Thanks guys for the feedback. Found a solution in completely recreating the UINavigationController's behavior. When I was nearly finished I ran into Nick Lockwood's solution:
https://github.com/nicklockwood/OSNavigationController
OSNavigationController is a open source re-implementation of UINavigationController. It currently features only a subset of the functionality of UINavigationController, but the long-term aim is to replicate 100% of the features.
OSNavigationController is not really intended to be used as-is. The idea is that you can fork it and then easily customize its appearance and behaviour to suit any special requirements that your app may have. Customizing OSNavigationController is much simpler than trying to customize UINavigationController due to the fact that the code is open and you don't need to worry about private methods, undocumented behavior, or implementation changes between versions.
By overriding my UINavigationController with his code, I was able to work with background images in UINavigationcontrollers
Thanks!
Solution 2
I managed to workaround the new transition type by creating a category for UINavigationController
. In my case I needed to revert it to the old transition style because I have transparent viewControllers that slide over a static background.
-
UINavigationController+Retro.h
@interface UINavigationController (Retro) - (void)pushViewControllerRetro:(UIViewController *)viewController; - (void)popViewControllerRetro; @end
-
UINavigationController+Retro.m
#import "UINavigationController+Retro.h" @implementation UINavigationController (Retro) - (void)pushViewControllerRetro:(UIViewController *)viewController { CATransition *transition = [CATransition animation]; transition.duration = 0.25; transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; transition.type = kCATransitionPush; transition.subtype = kCATransitionFromRight; [self.view.layer addAnimation:transition forKey:nil]; [self pushViewController:viewController animated:NO]; } - (void)popViewControllerRetro { CATransition *transition = [CATransition animation]; transition.duration = 0.25; transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; transition.type = kCATransitionPush; transition.subtype = kCATransitionFromLeft; [self.view.layer addAnimation:transition forKey:nil]; [self popViewControllerAnimated:NO]; } @end
Solution 3
I have the same problem with clear background colors and crappy animations, so I create custom transitioning for ViewController with new iOS7 API. All you need is simply set a delegate for your navigation controller:
// NavigationController does not retain delegate, so you should hold it.
self.navigationController.delegate = self.navigationTransitioningDelegate;
Just add this files into your project: MGNavigationTransitioningDelegate.
Solution 4
I had a problem where when UIViewController A did a pushViewController to push UIViewController B, the push animation would stop at about 25%, halt, and then slide B in the rest of the way.
This DID NOT happen on iOS 6, but as soon as I started using iOS 7 as the base SDK in XCode 5, this started happening.
The fix is that view controller B did not have a backgroundColor set on its root view (the root view is the one that is the value of viewController.view, that you typically set in loadView). Setting a backgroundColor in that root view's initializer fixed the problem.
I managed to fix this as follows:
// CASE 1: The root view for a UIViewController subclass that had a halting animation
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
// Do some initialization ...
// self.backgroundColor was NOT being set
// and animation in pushViewController was slow and stopped at 25% and paused
}
return self;
}
// CASE 2: HERE IS THE FIX
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
// Do some initialization ...
// Set self.backgroundColor for the fix!
// and animation in pushViewController is no longer slow and and no longer stopped at 25% and paused
self.backgroundColor = [UIColor whiteColor]; // or some other non-clear color
}
return self;
}
Solution 5
First of, I'm not using Storyboard. I tried using UINavigationController+Retro. For some reason, the UINavigationController is having a hard time releasing the UIViewController at the top of the stack. Here's the solution that works for me using iOS 7 custom transition.
-
Set delegate to self.
navigationController.delegate = self;
-
Declare this UINavigationControllerDelegate.
- (id<UIViewControllerAnimatedTransitioning>)navigationController (UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { TransitionAnimator *animator = [TransitionAnimator new]; animator.presenting = YES; return animator; }
Note that it'll only get called when animated is set to YES. For example
[navigationController pushViewController:currentViewController animated:YES];
-
Create the animator class extending NSObject. I called mine TransitionAnimator, which was modified from TeehanLax's TLTransitionAnimator inside UIViewController-Transitions-Example.
TransitionAnimator.h
#import <Foundation/Foundation.h> @interface TransitionAnimator : NSObject <UIViewControllerAnimatedTransitioning> @property (nonatomic, assign, getter = isPresenting) BOOL presenting; @end
TransitionAnimator.m
#import "TransitionAnimator.h" @implementation TransitionAnimator - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return 0.5f; } - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{ UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; if (self.presenting) { //ANIMATE VC ENTERING FROM THE RIGHT SIDE OF THE SCREEN [transitionContext.containerView addSubview:fromVC.view]; [transitionContext.containerView addSubview:toVC.view]; toVC.view.frame = CGRectMake(0, 0, 2*APP_W0, APP_H0); //SET ORIGINAL POSITION toVC OFF TO THE RIGHT [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ fromVC.view.frame = CGRectMake(0, (-1)*APP_W0, APP_W0, APP_H0); //MOVE fromVC OFF TO THE LEFT toVC.view.frame = CGRectMake(0, 0, APP_W0, APP_H0); //ANIMATE toVC IN TO OCCUPY THE SCREEN } completion:^(BOOL finished) { [transitionContext completeTransition:YES]; }]; }else{ //ANIMATE VC EXITING TO THE RIGHT SIDE OF THE SCREEN } } @end
Use presenting flag to set the direction you want to animate or which ever condition you prefer. Here's the link to Apple reference.
Bart van den Berg
Updated on June 05, 2020Comments
-
Bart van den Berg almost 4 years
So. Just started transitioning my IOS code to IOS7, and ran into a bit of problem.
I've got a
UINavigationController
, which has child ViewControllers and I'm usingpushViewController
to display the next views. To create a parallax animation with a set of images, if customized theUINavigationController
to animate a set ofUIImageViews
and my child ViewControllers all have aself.backgroundColor = [UIColor clearColor]
, transparency.Since iOS7, the way the
UINavController
animates it child vc's, is updated, by partially moving the current view controller and on top pushing the new viewcontroller, my parallax animation looks crap. I see the previous VC move a bit and then disappear. Is there any way I can restore the previousUINavigationController
pushViewController animation? I can't seem to find this in the code.WelcomeLoginViewController* welcomeLoginViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"WelcomeLogin"]; [self.navigationController pushViewController:welcomeLoginViewController animated:YES];
Even tried using:
[UIView animateWithDuration:0.75 animations:^{ [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [self.navigationController pushViewController:welcomeLoginViewController animated:NO]; [UIView setAnimationTransition:<specific_animation_form> forView:self.navigationController.view cache:NO]; }];
Does anyone have any clue?
-
Bart van den Berg over 10 yearsHi Arne, Thanks for your solution. However you animate the UINavigationController's view instead of the child views of the controller. That way, the navigation bar exhibits different behavior. Tried solving by creating a completely custom animations of both views, but still not getting the desired effect.
-
mongeta over 10 yearsI'm having exactly the same problem, but unfortunately my view B has clearColor, I've just have to add exactly the same image background as self.parentViewController.view.xxxxxx and now it works... thanks!
-
GenesisST over 10 yearsUnless I am wrong, this seems to only work with presenting a view controller. I could not make it work when pushing a view controller to a navigation controller.
-
Bill over 10 yearsThanks, this works for me. However, when the user taps the Back button, it reverts to the busted animation (because the back button doesn't call popViewControllerRetro).
-
jcsahnwaldt Reinstate Monica over 10 yearsBill, you can replace the back button with
leftBarButtonItem
. I chose transition.duration = 0.4 because it seemed to be closer to the old speed, at least on the Xcode simulators. -
Bill Cheswick over 10 yearsYes, this exactly fixed the problem. Thanks!
-
Miros over 10 yearsThis did the trick for me - I also had a viewcontroller with transparent background being pushed onto NavigationController with transparent background and a background image. The new iOS7 transition looked like crap, I'm wondering why Apple cannot just add those new features "silently", without "brute-enforcing" changes to native UI behaviour - gives us all a lot of trouble and headaches!
-
weienw over 10 yearsThis worked wonderfully for me. I was using clearColor for my viewControllers (in order to get smooth animation underneath on UIWindow subviews), but got an annoying random stutter until implementing this.
-
uerceg over 10 years@Bill: Check my answer below.
-
iOSAppDev over 10 yearsThanks for this great solution... I noticed one thing , when I call pushViewControllerRetro with time interval 0.4 I can see black color while transition. Any one saw this behavior ? Also simple popViewControler:animated works for me. So still do I need to use popViewControllerRetro ? Thanks
-
CedricSoubrie over 10 yearsCrazy solution. Worked for me also !
-
ttotto about 10 yearsGreat. One thing is no need to use presenting variable. UINavigationControllerOperation indicates this operation is Push or Pop.
-
Martin Herman almost 10 years@iOSAppDev, I'm seeing the same thing! The black and white colours... anyone a fix?
-
iOSAppDev almost 10 years@Martin I didn't get any solution yet
-
Martin Herman almost 10 years@iOSAppDev I think we should create a new question for that... After slowing it down to around 1 sec, I've found that it is because the old VC flashes white and the new one too. Check it out. By any chance, did you find some other solution?
-
Martin Herman almost 10 years@iOSAppDev - I found the solution. What I and probably you did, is add a background layer to the UINavigationController
[_navController.view insertSubview:_someSubview atIndex:0];
... Now what I do is add the background view to the UIWindow as a subview instead...[self.window addSubview:_someView];
and call[self.view setBackgroundColor:[UIColor clearColor]];
in everyviewDidAppear
of the UIViewController being pushed. Works fabulously! :) -
Gerardo almost 10 yearsAmazing! Best solution
-
amar almost 10 yearsHow did you get that? i could have tried Voodoo instead..thanks
-
jestro about 9 yearsWorks like a charm! Thank you!
-
Sagar D almost 8 yearsVery useful! Thank you!
-
Tom almost 8 yearsUnfortunately this solution breaks autolayout - when you rotate and go back the views are all broken on the previous page until you rotate again.
-
Sergey Teryokhin about 7 yearsGood solution. I made Swift 3 version of your gist: gist.github.com/steryokhin/f136bd3e0c38b0fcdbff30b311714257
-
ArtFeel about 7 years@Tom Maybe this would help gist.github.com/ArtFeel/7690431#gistcomment-1912196