Nowadays, good design is so much more than how an app looks. It also has to “feel” right.

If you take a look at the animations and transitions in iOS, you will see that the interaction with an app is almost as important to the whole experience.

It is much more than just the visuals. Take a look at this post to see how custom transitions work in Swift using Autolayout

How to Use Transitions To Give Your App the Wow Factor

So how do you bring your app into the new age. iOS does have an answer for us.

One of the things I love most about the features launched in iOS 7 is the new Transitioning APIs. I’m sure you have seen those cool animated transitions in apps like Evernote and the National Geographic app. You get to see cool rotations, zooms and animations on the navigation bar and you wondered, how the *!?* did they do that?

Take a look at the transitions in this video.

Prior to iOS 7, it was a pain to implement custom transitions so this is a welcome addition. In some of our new templates, we implement a couple of custom transitions that give your app that unique edge.

You can find this in our new iOS templates Highlights and Trackbeam.

With custom transitions, you can display modal views or navigation hierarchy views that are pushed on the stack. I decided to extract the transition code into a simple smaller project and show you how we did it.

How Did We Do This?

Here’s the walkthrough.

First off, your initial View Controller (the one who initiates the transition) need to implement the UIViewControllerTransitioningDelegate protocol.

Most importantly, these two methods will be implemented
animationControllerForPresentedController:presentingController:sourceController:
and animationControllerForDismissedController:

The first method asks your View Controller for the suitable object to control the animation during presentation. The second method asks for another object that controls the animation during dismissal.

- (id)animationControllerForPresentedController:(UIViewController *)presented
                                                                  presentingController:(UIViewController *)presenting
                                                                      sourceController:(UIViewController *)source
{
    self.animationController.isPresenting = YES;
 
    return self.animationController;
}
 
- (id )animationControllerForDismissedController:(UIViewController *)dismissed {
    self.animationController.isPresenting = NO;
 
    return self.animationController;
}

These can be same object as long as you specify behaviours for both presentation and dismissal.

The Real Mccoy: The Animation Controller

Now we need to implement this controller class, the culprit for controlling the animations. It conforms to the UIViewControllerAnimatedTransitioning protocol. There is also a similar protocol for interactive transitions but let’s make it simple for now.

In our case, we have a class called the DropAnimation controller and it implements a method called animationTransition.

This is the method below. It checks whether we are presenting or dismissing a controller, then executes the correct method.

-(void)animateTransition:(id)transitionContext{
 
    if(self.isPresenting){
        [self executePresentationAnimation:transitionContext];
    }
    else{
        [self executeDismissalAnimation:transitionContext];
    }
 
}

Now this is where the magic happens. The executePresentationAnimation method adds the new controller to the stage and then uses the new Spring animation methods on UIView to push in the view from the top as a modal view.

-(void)executePresentationAnimation:(id)transitionContext{
 
    UIView* inView = [transitionContext containerView];
 
    UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
 
    [inView addSubview:toViewController.view];
 
    CGPoint centerOffScreen = inView.center;
 
    centerOffScreen.y = (-1)*inView.frame.size.height;
    toViewController.view.center = centerOffScreen;
 
    [UIView animateWithDuration:self.presentationDuration delay:0.0f usingSpringWithDamping:0.4f initialSpringVelocity:6.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
 
        toViewController.view.center = inView.center;
 
    } completion:^(BOOL finished) {
 
        [transitionContext completeTransition:YES];
    }];
}

And when dismissing, the reverse happens. We swing the view back up the stage and out of view.

-(void)executeDismissalAnimation:(id)transitionContext{
 
    UIView* inView = [transitionContext containerView];
 
    UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
 
    UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
 
    [inView insertSubview:toViewController.view belowSubview:fromViewController.view];
 
    CGPoint centerOffScreen = inView.center;
    centerOffScreen.y = (-1)*inView.frame.size.height;
 
    [UIView animateKeyframesWithDuration:self.dismissalDuration delay:0.0f options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
 
        [UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.5 animations:^{
 
            CGPoint center = fromViewController.view.center;
            center.y += 50;
            fromViewController.view.center = center;
        }];
 
        [UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^{
 
            fromViewController.view.center = centerOffScreen;
 
        }];
 
 
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:YES];
    }];
}

Get the source code

I hope you are as excited to get new transitions into your app. Check out the Eventer template to get the full featured design with custom animations.

You can download the sample animation I just described above here

28 comments

  1. Jon

    Hi Tope, How can I pass data between the ViewControllers? If I use segue the animation will not work …

    Thank !

      • Jesse Wolff

        UIModalPresentationCustom is not related to this. You set it only if you want the fromViewController to stay around after the transition is complete.

    • Jesse Wolff

      It does work. The way you pass data to a view controller doesn’t change when you use the new custom animated transitions in iOS 7. So you would still setup the view controller in prepareForSegue if you’re using storyboards.

    • Tope

      Wow, Jesse, Nice work!. This is something a lot of developers were looking for when the Transitions were demo-ed at WWDC.

      Thanks for linking to it.

      Cheers,
      Tope

  2. Larry

    Can this be easily implemented with a navigation controller as well? I implemented this, but my navigation bar is missing when the new controller is presented.

    • Tope

      Yes, you can. Instead of implementing the UIViewTransitioningDelegate methods, you can use the UINavigationControllerDelegate method below.

      – (id)navigationController:(UINavigationController *)navigationController
      animationControllerForOperation:(UINavigationControllerOperation)operation
      fromViewController:(UIViewController *)fromVC
      toViewController:(UIViewController *)toVC
      {
      self.animationController.isPresenting = fromVC == self;
      return self.animationController;
      }

  3. AppVizzle

    I’ve tried implementing this into my app, though when I go to run it I get this error

    duplicate symbol _OBJC_IVAR_$_RecipeBookViewController.recipes in:
    /Users/hd/Library/Developer/Xcode/DerivedData/RecipeBook-csrkajlnmmsksydjeyttubffffck/Build/Intermediates/RecipeBook.build/Debug-iphoneos/Ultimate Secret Menu.build/Objects-normal/armv7s/RecipeBookViewController.o
    /Users/hd/Library/Developer/Xcode/DerivedData/RecipeBook-csrkajlnmmsksydjeyttubffffck/Build/Intermediates/RecipeBook.build/Debug-iphoneos/Ultimate Secret Menu.build/Objects-normal/armv7s/AboutViewController.o
    duplicate symbol _OBJC_IVAR_$_RecipeBookViewController._tableView in:
    /Users/hd/Library/Developer/Xcode/DerivedData/RecipeBook-csrkajlnmmsksydjeyttubffffck/Build/Intermediates/RecipeBook.build/Debug-iphoneos/Ultimate Secret Menu.build/Objects-normal/armv7s/RecipeBookViewController.o
    /Users/hd/Library/Developer/Xcode/DerivedData/RecipeBook-csrkajlnmmsksydjeyttubffffck/Build/Intermediates/RecipeBook.build/Debug-iphoneos/Ultimate Secret Menu.build/Objects-normal/armv7s/AboutViewController.o
    duplicate symbol _OBJC_IVAR_$_RecipeBookViewController._scrollView in:
    /Users/hd/Library/Developer/Xcode/DerivedData/RecipeBook-csrkajlnmmsksydjeyttubffffck/Build/Intermediates/RecipeBook.build/Debug-iphoneos/Ultimate Secret Menu.build/Objects-normal/armv7s/RecipeBookViewController.o
    /Users/hd/Library/Developer/Xcode/DerivedData/RecipeBook-csrkajlnmmsksydjeyttubffffck/Build/Intermediates/RecipeBook.build/Debug-iphoneos/Ultimate Secret Menu.build/Objects-normal/armv7s/AboutViewController.o
    duplicate symbol _OBJC_CLASS_$_RecipeBookViewController in:
    /Users/hd/Library/Developer/Xcode/DerivedData/RecipeBook-csrkajlnmmsksydjeyttubffffck/Build/Intermediates/RecipeBook.build/Debug-iphoneos/Ultimate Secret Menu.build/Objects-normal/armv7s/RecipeBookViewController.o
    /Users/hd/Library/Developer/Xcode/DerivedData/RecipeBook-csrkajlnmmsksydjeyttubffffck/Build/Intermediates/RecipeBook.build/Debug-iphoneos/Ultimate Secret Menu.build/Objects-normal/armv7s/AboutViewController.o
    duplicate symbol _OBJC_METACLASS_$_RecipeBookViewController in:
    /Users/hd/Library/Developer/Xcode/DerivedData/RecipeBook-csrkajlnmmsksydjeyttubffffck/Build/Intermediates/RecipeBook.build/Debug-iphoneos/Ultimate Secret Menu.build/Objects-normal/armv7s/RecipeBookViewController.o
    /Users/hd/Library/Developer/Xcode/DerivedData/RecipeBook-csrkajlnmmsksydjeyttubffffck/Build/Intermediates/RecipeBook.build/Debug-iphoneos/Ultimate Secret Menu.build/Objects-normal/armv7s/AboutViewController.o
    ld: 5 duplicate symbols for architecture armv7s

    clang: error: linker command failed with exit code 1 (use -v to see invocation)

  4. Web Developer

    Your MO to get subscriptions in exchange for code sample is a little annoying. I believe knowledge should be shared for free. Thanks

  5. sandeep

    Hi i am using you code in my sample but i am checking on landscape mode its not working . How we fixed this issue . i seen there when we in landscape mode my some button not selectable.
    how we fixed this issue.

  6. Russell

    I believe there might be a retain cycle when performing transition 1 multiple times in the demo app.

  7. nirav

    Hello,
    I am facing one problem and i have described it below.
    I have two view controller and i want to display second viewcontroller above first view controller , i have achieved it using your code , now the difficulty is that i want to make the secondview controller transparent so that first view controller can be displayed.

  8. nirav

    [UIView animateWithDuration:duration delay:(halfDuration – (0.3*halfDuration)) usingSpringWithDamping:0.7f initialSpringVelocity:6.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
    toViewController.view.frame = inView.frame;
    } completion:^(BOOL finished) {
    [self.transitionContext completeTransition:YES];
    }];
    This is the code in zoom animation controller , after executing [self.transitionContext completeTransition:YES]; , the part below secondview controller become black , so i can not see first view controller as i want to make transparent. Is there any solution ?
    Thanks