Or, you can follow with me step-by-step for a better understanding of what’s happening.

If you want to jump straight in you can download this sample project which includes a basic storyboard with two screens connected to each other, and a transition that animates by rotating the screens from the top-left point of the window.

…instead of relying on the handful of default animations that Apple provides . In the next I look at a more real life example of an animated menu .

This post looks at how to you can extend these animation techniques to create your own custom transitions like this…

Create a simple project

Lets start from scratch and create a simple storyboard project with two view controllers that we can navigate between by tapping on a button.

1. Create a new project

Use the ‘iOS Single View template’ which automatically creates a project with a storyboard and a single view controller.

2. Add a button to the screen

Also add constraints so that the button is fixed to the bottom, left and right edges of the screen by 20pts.

3. Duplicate the screen

Just like any other editor, in Xcode you can ⌘-C to copy and ⌘-P to paste objects in your storyboard.

4. Create a segue between the screens

Option-click on a button on one screen and drag over to the other screen and release to create a segue.

Pick ‘custom’ when promoted to choose a type of segue.

Change the button text to read ‘Hide’ and change the color of the background and button text.

6. Run the project!

By now you should be able to run the project and tap on the Show button to present the second screen.

Because we’ve not specified any information for the transition yet, iOS defaults to the default transition animation of the second screen sliding up from the bottom.

Tapping on the Hide button does nothing yet. So the next step is to fix that…

Aside: Segues & Exit Segues

When we present a view controller in iOS with a segue it may seem like we’re replacing that screen, but actually we’re just adding a new screen onto a stack of screens in which only the last screen in that stack is visible.

If it’s a new concept it can be tricky at first to understand so lets take a closer look.

Consider a two screens that are connected to each other with two segues that we’ll call “Blue” and “Red”. Segue Blue presents the dark colored screen and Segue Red presents the light colored screen.

If we start the app on the light colored screen and perform the Blue segue an instance of the dark colored screen is added to the stack of screens and we see the dark screen because it’s on top.

But if we continue perfuming segues we just end up adding more and more screens onto our stack of view controllers.

It may seem like we’re replacing the screens with each segue because all we see is the screen that’s on the top of the stack but with each segue the stack is growing larger and larger.

Eventually, if this is not handled the app will eventually crash because each screen requires memory to be allocated to it and there is always finite amount of memory to use.

Instead of performing a regular segue to attempt to return to an earlier screen we need to create an exit segue that allows us to unwind to an earlier screen in the stack.

7. Attempt Exit segue

Creating an exit segue is similar to a regular segue in that you start by selecting the object you want to trigger to segue but instead of dragging to another view controller you control-click and drag and release on the screens exit segue icon.

We can try that now…

… but nothing will happen when we release the drag because we need to do a little setting up first.

8. Create an exit point

To create an exit segue, we need to create at least one IBAction in the view controller file that we want to return to.

We can choose any name we want for the function, but it needs to be an @IBAction and take a UIStoryboardSegue as a parameter.

Here’s the method that I created. Note that to perform the exit segue the method just needs to exist, we don’t need to have any code in the function body.

@IBAction func unwindToViewController (sender: UIStoryboardSegue){ }

Now when we return to our storyboard file and drag onto the exit segue button a pop up menu gives us an option to connect the IBAction we just created.

9. Run the project again

Now when we run the project, tapping the Hide button removes the screen that’s currently on the stack and shows us the screen that’s below.

Custom Transition Animations

Now that we have a simple project with the default transitions between two screens, how do we go about creating a custom transition animation?

General approach

The general approach is to create an “animator” object that is responsible for every type of animation we want to show.

Then we need to tell iOS when those animator objects should be used instead of using a default transition.

Both of these are tasks are achieved by using a pattern object-oriented programming pattern called protocols.

To create the animator we need an object that adheres to the UIViewControllerAnimatedTransitioning protocol and to tell iOS when those animators should be used we need an object that adheres to the UIViewControllerTransitioningDelegate protocol.

There are multiple ways that this could be achieved, but the approach that I’m going to show you is to create an object that will meet the requirements of both of these protocols. We’ll call it a TransitionManager .

1. Create the TransitionManager class

Create a new file in your project by pressing ⌘-N and choose ‘Cocoa Touch Class’ in the first dialog and enter the class TransitionManager and subclass NSObject .

This will create a new Swift file that looks something like this:

// // TransitionManager.swift // Transition // import UIKit class TransitionManager: NSObject { }

2. Enter protocol methods

To meet the requirements of these two protocols, our TransitionManager class needs to implement a total of four methods.

The first two methods meet the requirements of being an animator:

animateTransition defines the animations that transition from one screen to another.

transitionDuration returns the number of seconds that the animation takes.

The final two meet the requirements of being the “transition delegate” by returning which animator should be used for either presenting or dismissing a screen:

animationControllerForPresentedController

animationControllerForDismissedController

Here’s the updated TransitionManager class with these four methods completed except for the animateTransition which we still have to complete.

// // TransitionManager.swift // Transition // import UIKit class TransitionManager: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate { // MARK: UIViewControllerAnimatedTransitioning protocol methods // animate a change from one viewcontroller to another func animateTransition(transitionContext: UIViewControllerContextTransitioning) { // TODO: Perform the animation } // return how many seconds the transiton animation will take func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval { return 0.5 } // MARK: UIViewControllerTransitioningDelegate protocol methods // return the animataor when presenting a viewcontroller // remmeber that an animator (or animation controller) is any object that aheres to the UIViewControllerAnimatedTransitioning protocol func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return self } // return the animator used when dismissing from a viewcontroller func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return self } }

3. Complete animateTransition method

Lets complete the animateTransition function to perform an transition where the new screen slides in from the right as the old screen slides off to the left.

Here’s the animateTransition method completed to do this:

// animate a change from one viewcontroller to another func animateTransition(transitionContext: UIViewControllerContextTransitioning) { // get reference to our fromView, toView and the container view that we should perform the transition in let container = transitionContext.containerView() let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)! let toView = transitionContext.viewForKey(UITransitionContextToViewKey)! // set up from 2D transforms that we'll use in the animation let offScreenRight = CGAffineTransformMakeTranslation(container.frame.width, 0) let offScreenLeft = CGAffineTransformMakeTranslation(-container.frame.width, 0) // start the toView to the right of the screen toView.transform = offScreenRight // add the both views to our view controller container.addSubview(toView) container.addSubview(fromView) // get the duration of the animation // DON'T just type '0.5s' -- the reason why won't make sense until the next post // but for now it's important to just follow this approach let duration = self.transitionDuration(transitionContext) // perform the animation! // for this example, just slid both fromView and toView to the left at the same time // meaning fromView is pushed off the screen and toView slides into view // we also use the block animation usingSpringWithDamping for a little bounce UIView.animateWithDuration(duration, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.8, options: nil, animations: { fromView.transform = offScreenLeft toView.transform = CGAffineTransformIdentity }, completion: { finished in // tell our transitionContext object that we've finished animating transitionContext.completeTransition(true) }) }

The important parts to notice are:

iOS passes us an object called transitionContext whenever this method is performed. This object gives us references to views of screens that we’re transitioning from and to and very importantly gives us a third view that acts as a container for the animations to be performed in.

whenever this method is performed. This object gives us references to views of screens that we’re transitioning from and to and very importantly gives us a third view that acts as a container for the animations to be performed in. We have to manually add our fromView and toView to the container view. The order we add our views determines which will be shown on top if they overlap as part of the animation.

We use a regular block animation to perform the animation, but instead of entering the animation duration we call the method self.transitionDuration(transitionContext) .

. When we’ve completed the animation we call the method transitionContext.completeTransition(true) .

Before we run the project we need to update our ViewController class and create an instance of the our TransitionManager object.

// add this right above your viewDidLoad function... let transitionManager = TransitionManager()

And add a prepareForSegue method which is called whenever iOS is preparing to transition from one screen to another. This is a good point to tell iOS that we want to use our transition manager instead of a default transition animation.

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { // this gets a reference to the screen that we're about to transition to let toViewController = segue.destinationViewController as UIViewController // instead of using the default transition animation, we'll ask // the segue to use our custom TransitionManager object to manage the transition animation toViewController.transitioningDelegate = self.transitionManager }

Now when we run the project you should see a slide transition instead of the default transition.

But the animation is the exactly the same for both presenting and dismissing the screen!

4. Reversing the transition

To reverse the transition we just need to add a little logic to our TransitionManager to keep track of if we’re presenting a screen or dismissing a screen and update the animation accordingly.

To do this, lets add a variable to our TransitionManager class called presenting .

In the animationControllerForPresentedController set presenting to be true, and in animationControllerForDismissedController set it to be false.

Then in the animateTransition method check to see if presenting is true or false and update the animation accordingly.

Try this on your own, if you get stuck, here’s a diff that looks at the changes I made to update TransitionMaster at this step, or you can download the entire updated project.

Running the project with these updates the transition animation should now reverse when dismissing a screen.

5. A more complex animation

Now lets update the transformations in animateTransition so that instead of the screen sliding in, they rotate in. Here’s are the most important changes to the method:

// set up from 2D transforms that we'll use in the animation let π : CGFloat = 3.14159265359 let offScreenRotateIn = CGAffineTransformMakeRotation(-π/2) let offScreenRotateOut = CGAffineTransformMakeRotation(π/2) // set the start location of toView depending if we're presenting or not toView.transform = self.presenting ? offScreenRotateIn : offScreenRotateOut // set the anchor point so that rotations happen from the top-left corner toView.layer.anchorPoint = CGPoint(x:0, y:0) fromView.layer.anchorPoint = CGPoint(x:0, y:0) // updating the anchor point also moves the position to we have to move the center position to the top-left to compensate toView.layer.position = CGPoint(x:0, y:0) fromView.layer.position = CGPoint(x:0, y:0)

Running the project now you should see a transition with the screens rotating in and out instead of a slide.

Note that with this animation there are times where only a small part of each screen is visible and we see a black background underneath. This is the default background color of our container view and can be updated if needed.

7. Updating the status bar style

For a final touch lets override the preferredStatusBarStyle method in our ViewController class so that the status bar style switches from default to light depending if we’re presenting a screen or not.

// we override this method to manage what style status bar is shown override func preferredStatusBarStyle() -> UIStatusBarStyle { return self.presentingViewController == nil ? UIStatusBarStyle.Default : UIStatusBarStyle.LightContent }

Next steps

Hopefully this has given you a good starting point for creating your own custom animated transitions.

The samples I’ve given have created a transition that only uses the views of the screens that we’re transitioning from and to, but remember that the container view that the animation is performed in is just a regular UIView and we can add anything we want to it.

Also, the animations have all been 2D transforms but 3D transforms can be created when you animate the views layer object.

We’ve looked specifically at a modal transition, there are some differences when creating a transition for a push navigation or a changing screens in a tab bar but the general approach is the same.

The next post uses this technique to create an animated menu.

Soon I’m going to write about creating interactive transitions (where a transition completes interactively with a gesture), and transitions that have visual continuity between screens (for example an element from the first screen that persists to the next).

Follow me at @permakittens to get updates on new posts!

Notes