Wednesday, October 13, 2010

Changing the transition animation for an UINavigationController

I love the UINavigationController class for iOS. It's a great framework for managing a wide range of view-swapping applications, and we get the transition between views for free.

But, what if we want to use a different type of transition animation? UINavigationController only has one option--the new view slides in from the right while the old view slides off the left. There's no direct mechanism, but it turns out that it's not too hard to manage, at least for a select set of transitions.

First, lets look at the UIView methods. The navigation controller has its own view, which will contain the subviews we wish to manage. in iOS 4.0 and later, we can use that view's block-based animation.

[UIView 
transitionWithView:self.navigationController.view
duration:1.0
options:UIViewAnimationOptionTransitionCurlDown
animations:^{
[self.navigationController
pushViewController:self.alternateView
animated:NO];
}
completion:NULL];


Note: when we actually call pushViewController: we are setting the animated value to NO. This disables the Navigation Controller's default animation. Instead, the block of code uses the defined transition (in this case UIViewAnimationOptionTransitionCurlDown).

The same thing can be done using iOS 2.0 or later (although, you cannot submit anything older than 2.0 at this time).

[UIView beginAnimations:@"transition" context:nil];
[UIView setAnimationDuration:1.0];

[UIView
setAnimationTransition:UIViewAnimationTransitionCurlDown
forView:self.navigationController.view
cache:NO];

[self.navigationController
pushViewController:self.alternateView animated:NO];

[UIView commitAnimations];


Also notice, the two methods use two slightly different constants to define the view type. However, they both support the same four options: curl up, curl down, flip left and flip right.

We can get a different set of transitions by setting a transition animation for the Navigation Controller's view's Core Animation Layer.


CATransition* transition = [CATransition animation];
transition.duration = 1.0;
transition.type = kCATransitionFade;
transition.subtype = kCATransitionFromTop;

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

[self.navigationController
pushViewController:self.alternateView animated:NO];


There's one big difference here. The other animations were local. They only affected the code within the block (or between the beginAnimations and commitAnimations calls). This is a global change, and will continue to affect the Navigation Controller's behavior until it is changed again (or cleared by setting the animation to nil).

This also has four options: fade in, move in, push or reveal. For all but the fade, you can also set the direction of motion (from the top, bottom, left or right--though the iPhone seems to reverse the top and bottom options). We've already seen the push animation--the default behavior is a push from the right. Move in simply slides the new view over the existing one. Reveal pulls the old view away, revealing the existing one. While fade simply cross-fades from one view to the next.

I really wish there was an easy way to do other animated transitions (for example, having a view appear to grow from the center of the screen, or shrink into the center of the screen). But, I suspect those would take a lot of work at the CALayer level. I made a few attempts using regular animation blocks and altering the size of the incoming view. I could get the size to animate, but the old view simply vanished, leaving my new view to appear over a white background. Not what I wanted at all.

Still, there's a lot of cool effects you could produce with just these options. Happy hacking!

-Rich-

8 comments:

GeRyCh said...

Thank you for a great work that you did! I don't understand how people hasn't commented it yet! o_O It's so necessary for daily programming! Thanks

Mark Bonano said...

Great job! Thanks for posting.

@whisk_it said...

Love it! Thank you.

NithiN Chacko said...

Great work.. really helped me in my project...Thanks..!!

Justin said...

Very cool. Thanks for posting!

Tim said...

Thanks for sharing. Really helped me!

Robert patel said...
This comment has been removed by the author.
Robert patel said...

The simple reason that I like your writing is that you have shown me the way to shine more as a freelancer. I have great qualifications and can design the best website on the planet. Web designing is in my blood. With your push (through your write-up), I have begun contributing my work on websites like www.fiverr.com, blog commenting service,website content writer. Now, I am all ready to get, set…and go!