The release of iOS 7 is a breath of fresh air for both development and aesthetics (which I personally love). We can finally create stunning animations for our apps, so I think it’s time to write about how to do it.
If you remember my previous post about custom transitions you already know the hard part was drawing a “fake” view using the graphic representation of the views we wanted to animate.
Starting from iOS 7 we can follow a new path suggested by Apple. That’s exactly what I’m going to show you here, but before diving into the code, let’s see a quick preview of the final result of this tutorial.
This application is extremely simple: you move between views swiping from left to right.
Just keep in mind that for this example we are not implementing a stack of views, but we are just presenting and removing a modal view applying animations to both the original and the modal view itself.
The custom transition Structure
The trickiest challenge you’ll be facing here is to understand how the new objects/protocols involved in this process cooperate with each other.
Here are the three main actors:
– The protocol UIViewControllerTransitioningDelegate (we’ll just call it TransitionDelegate from now on)
– The protocol UIViewControllerAnimatedTransitioning (AnimatedTransitioning)
– The protocol UIViewControllerContextTransitioning
(ContextTransitioning)
These three protocols have some really important and different tasks. Let’s open the file MainViewController.m and see the first of them.
The main ViewController implements the protocol TransitionDelegate (the first actor). Its task is to define who is going to manage a transition when it happens.
At the end of the file you can see its implementation:
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source{
self.transitionManager.transitionTo = MODAL;
return self.transitionManager;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
self.transitionManager.transitionTo = INITIAL;
return self.transitionManager;
}
We return an object of TransitionManager class and we apply some settings to it… nothing more.
Opening the file TransitionManager.m/.h we have a deeper overview of the class whose instance is returned by the TransitionDelegete implementation.
First, it implements the protocol AnimatedTransitioning (the second actor). The role of this protocol is to “draw” the animations that take place when the transition happens.
We implement this protocol through the functions transitionDuration and animateTransition. The first defines how long the transition should take to complete and the second, finally, defines the transition animations.
These functions receive as input an object compliant to the ContexTransitioning protocol (the third actor). This object has some really important information that we are going to use during the transition.
Focusing on the first three lines of the function animateTransition you see:
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
CGRect sourceRect = [transitionContext initialFrameForViewController:fromVC];
The transitionContext object has information about the current transition like the current active view controller (fromVC), the view controller that is going to be presented next(toVC) and the size of the initial frame (sourceRect).
The rest of the code of this function is responsible of performing the animation. We’ll get back to it later.
Now open the file MainViewController.m and check the function I have named presentInfo.
-(void)presentInfo:(UIGestureRecognizer*)gesture{
ModalViewController *modal = [[ModalViewController alloc]init];
modal.transitioningDelegate = self;
modal.modalPresentationStyle = UIModalPresentationCustom;
[self presentViewController:modal animated:YES completion:^{
}];
}
This function is called when the user swipes from left to right over the first view.
It creates a new viewController of ModalViewController type and it presents it to the user.
First, it sets the presentation style for the modalController with UIModalPresentationCustom, then it defines the mainController as the delegate of the transition for the modal view and finally it presents the modal view using presentViewController:animated:completion.
By now when the modal view is presented or dismissed the mainController as TransitionDelegate will be asked to return an AnimatedTransitioning to perform the transition.
Take a breath
All this jumping from one class to another could be a little difficult to get at first. Let’s summarize the full sequence that will present the modal view.
1) The function showInfo sets the mainVC as transitionDelegate for modalVC
2) The modalVC need to be presented
3) Since modalVC has a transitionDelegate (point 1), this delegate is asked to return an object compliant to the AnimatedTransitioning protocol
4) The AnimatedTransitioning implementer will be asked to setup a transition duration and to design the transition receiving a TransitionContext object which carries information about the views that are going to be switched.
The animation
The object responsible for animating the transition is an instance of the TransitionManager Class, the animation is performed within the function animateTransition.
We’ve already seen the first part of this function, there we get information from the transition context.
Let’s move to Step 2. At this point we have to decide which animation we want to show. When the user first swipes from left to right we present the modal view. On the contrary, when the modal view is shown and the user swipes from right to left the initial view comes back.
Opening the file mainViewController.m and taking a look at the UIViewControllerTransitionDelegate functions at the end of the file, you’ll see that before returning the transitionManager reference we set a parameter for this object. The param transitionTo is just a simple way to know which transition we have to perform.
Go back to TransitionManager.m file to the function animationTransition. For the sake of clarity I have split the code Step 2 in two blocks.
The Step 2.A is called when the modal view is presented while Step 2.B when it’s is dismissed.
We can just look at the code for the block 2.A:
//STEP 2A: From the First View(INITIAL) -> to the Second View(MODAL)
if(self.transitionTo == MODAL){
//1.Settings for the fromVC ............................
CGAffineTransform rotation;
rotation = CGAffineTransformMakeRotation(M_PI);
fromVC.view.frame = sourceRect;
fromVC.view.layer.anchorPoint = CGPointMake(0.5, 0.0);
fromVC.view.layer.position = CGPointMake(160.0, 0);
//2.Insert the toVC view...........................
UIView *container = [transitionContext containerView];
[container insertSubview:toVC.view belowSubview:fromVC.view];
CGPoint final_toVC_Center = toVC.view.center;
toVC.view.center = CGPointMake(-sourceRect.size.width, sourceRect.size.height);
toVC.view.transform = CGAffineTransformMakeRotation(M_PI/2);
//3.Perform the animation...............................
[UIView animateWithDuration:1.0
delay:0.0
usingSpringWithDamping:.8
initialSpringVelocity:6.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
//Setup the final parameters of the 2 views
//the animation interpolates from the current parameters
//to the next values.
fromVC.view.transform = rotation;
toVC.view.center = final_toVC_Center;
toVC.view.transform = CGAffineTransformMakeRotation(0);
} completion:^(BOOL finished) {
//When the animation is completed call completeTransition
[transitionContext completeTransition:YES];
}];
}
In this block the fromVC refers to the initial view controller, while the toVC is the modal view controller. So in point 1 we define the final position of the initial view (fromVC) which in this case just rotates by 180°.
Point 2 shows a really important part of the transition: the containerView. This view is automatically generated by the context and it is a superview which contains the views involved into the transition. By default it contains the view of the currently active viewController (in this case fromVC), but we are responsible to add the view for the controller that is going to be presented (toVC) and placing it into the containerView.
Point 3 is where the animation takes place. As you can see it uses a function really similar to the classic animateWithDuration but it has some more parameters.
These parameter are the trick to the wow-effect for our animation. 🙂 Playing with these information we can modify the spring effect, obtaining some really interesting results.
The block animations contain the final transformation for fromVC and toVC. The function, just as its previous version, interpolates between the current and the new values creating the animations.
In this quick introduction I’ve shown you how to perform custom transitions using a modal view controller, but we can also work with UINavigationBar and UITabBar.
Another great feature is the ability to perform interactive transitions, but this could be the topic of another tutorial 🙂
Stay tuned!
Yari D'areglia
https://www.thinkandbuild.itSenior iOS developer @ Neato Robotics by day, game developer and wannabe artist @ Black Robot Games by night.