iOS

Introduction to Custom View Controller Transitions and Animations


Looking at the built in apps from Apple on your iOS device, you will notice the various animated transitions as you move from one view to another for example the way view controllers are presented in master-detail views with a swipe that slides the detail view controller over the master view controller as seen in the Messages app or Settings app and the various transitions that represent a segue to another view controller.

iOS 7 introduced custom view controller transitions which make it possible for developers to create their own animated transitions from one view controller to the next in their apps. In this tutorial, we’ll take a look at how to do this. We’ll also look at how to create gesture driven transitions called interactive transitions. To follow along, download the starter project which we’ll be using throughout the tutorial.

custom-view-transition

Getting Started

To create custom transitions you have to follow three steps:

  • Create a class that implements the UIViewControllerAnimatedTransitioning protocol. Here you will write code that performs the animation. This class is referred to as the animation controller.
  • Before presenting a view controller, set a class as its transitioning delegate. The delegate will get a callback for the animation controller to be used when presenting the view controller.
  • Implement the callback method to return an instance of the animation controller from the first step.

Run the starter project and you will be presented with a table view of a list of items. There is an Action button on the navigation bar and when you tap it you’ll be presented with another view that appears in the usual modal style of sliding up from the bottom. We will write a custom transition for this view.

custom view controller animation

Custom Present Transition

The first thing to do as stated previously, is to create the animation controller. Create a new class called CustomPresentAnimationController and make it a subclass of NSObject. Change its declaration as shown.

UIViewControllerAnimatedTransitioning protocol has two required methods which we’ll add next. Add the following methods to the class.

The first method specifies the length of the transition animation. For the demo app we’ve set it at 2.5 seconds, but you probably should set it to a smaller number in a real app.

In the second method we use the transitionContext to get the view controller we are navigating from, to, the final frame the transition context should have after the animation completes and the container view which houses the views corresponding to the to and from view controllers.

We then position the to view just below the bottom of the screen. Then we add the to view to the container view and in the animate closure, we animate the to view by setting its final frame to the location given by the transition context. We also animate the from view‘s alpha value so that as the to view is sliding up the screen over the from view, the from view will be faded out. The duration of the animation used is the one set in transitionDuration(transitionContext:). In the completion closure, we notify the transition context when the animation completes and then change the from view‘s alpha back to normal. The framework will then remove the from view from the container.

With the animation controller completed, we need to link it to a storyboard segue.

Open the ItemsTableViewController.swift file and change the class declaration as shown.

UIViewController has a property named transitionDelegate that supports custom transitions. When transitioning to a view controller, the framework checks this property to see if a custom transition should be used. UIViewControllerTransitioningDelegate supplies custom transitions.

Open Main.storyboard and select the Present modally segue to Action View Controller and in the Attributes Inspector, set its Identifier to showAction.

Custom View Controller Animation

Back in ItemsTableViewController add the following to the class.

Here we create an instance of our animation controller and then in the prepareForSegue() function, we detect the segue for the Action screen and set the transitionDelegate property of the destination view controller.

Add the following UIViewControllerTransitioningDelegate method to the class. This returns the instance of our custom animation controller.

Run the application and you should see the Action view slide up slowly from the screen and bounce a little before settling.

Custom View Controller Animation #3

If you want a slightly different effect, then change this statement in CustomPresentAnimationController.swift

to the statement below, which changes the original position of the to view controller to be above the screen.

Run the app and the Action view should fall from above.

Custom View Controller

Custom Dismiss Transition

We’ve set a custom transition for presenting our view, but when it is dismissed, it uses the default transition set by Apple.

Custom View Controller #4

The UIViewControllerTransitioningDelegate also allows you to specify an animation controller to use when dismissing a view controller as well as when presenting one. We’ll create this next.

Create a class named CustomDismissAnimationController that is a subclass of NSObject. Modify its declaration as shown.

Add the following to the class.

This is similar to the implementation of the presentation transition. In the animateTransition() function, we get the to and from view controllers. The to view controller here is the table view controller. We change its view’s alpha value so that it will start off as being faded when we start animating. We then add the view to the container and place it behind the from view controller’s view so that it won’t be visible just yet.

In the animation block, we animate the from view‘s size to have a width and height of 0, maintaining its center. This will have an effect of shrinking the from view to nothingness. We also animate the to view‘s alpha to being completely visible.

In ItemsTableViewController add the following property.

Add the following function to the class.

The UIViewControllerTransitioningDelegate protocol provides the above function which retrieves the animation controller of a dismissed view controller.

Run the app. You should see the following animation.

Custom View Animation #5

The animation isn’t what we expected. You can see the white frame of the from view shrinks as expected, but the image on the view doesn’t change in size. This is because changing the view’s frame doesn’t affect its children. We’ll fix this by using UIView snapshotting.

UIView snapshotting works by taking a snapshot of an existing UIView and rendering it into a lightweight UIView. We will then use this snapshot in out animation and not the actual view.

Replace the animateTransition() function with the following.

Here, we create a snapshot of the from view controller‘s view, add it to the container and remove the from view from the container. We then shrink this snapshot in our animation and when the animation completes, we remove the snapshot view from the container.

Run it and the animation should now run smoothly.

Custom View Controller Animation

Navigation controller transitions

We’ve looked at adding a custom transition for modal view controller presentation where we added a transitioning delegate to the presenting view controller. However, setting a delegate on every view controller can get tiresome when working with a UITabBarController or UINavigationController.

These controllers give a simpler approach whereby the animation controller for a transition is supplied via the UITabBarControllerDelegate or UINavigationControllerDelegate.

We’ll see this in action by adding a custom transition to a navigation controller.

To start off, we create an animation controller. Create a class called CustomNavigationAnimationController, make it a subclass of NSObject and change its declaration as follows.

Add the following to the class. I use a simplified version of this cube animation for this animation controller. The animation controller is set up as usual just like we’ve seen with the previous two animation controllers. Notice the reverse class variable. We use this to determine the direction of the animation, depending on whether we are moving from master to detail view or vice versa.

Open ItemsTableViewController.swift and modify the class declaration as follows.

UINavigationControllerDelegate supplies the animation controller.

Add the following property to the class.

Add the following at the end of viewDidLoad().

The above sets the host navigation controller’s delegate so that the new transition delegate methods can be received.

Then add the following to the class.

The above function is called to request for an animation controller to navigate between the from and to view controllers and it returns an instance of our animation controller. The direction of the transition is based on whether this is a push or pop navigation operation.

Run the app. Select a table view cell and you should see the animation below.

Custom View Animation #7

Making it Interactive

We’ll make the above transition interactive, i.e. the user will be able to control the transition with gestures.

iOS built-in apps come with this feature. As an alternative to the back button, you can initiate a transition by swiping from the left side of the screen. You can use a short swipe to briefly view the master view and then cancel the transition. A long swipe initiates the pop navigation operation.

Custom View Animation #8

To get started, we need an interaction controller. Interactive controllers use the UIViewControllerInteractiveTransitioning protocol. The navigation controller delegate or the transitioning delegate requests for an optional interaction controller after requesting an animation controller.

Let’s create the interaction controller. Create a new class and name it CustomInteractionController and make it a subclass of UIPercentDrivenInteractiveTransition.

UIPercentDrivenInteractiveTransition implements the UIViewControllerInteractiveTransitioning protocol so we wont have to add that to our class.

To use UIPercentDrivenInteractiveTransition, your animation controller must use a single UIView animation, so that the animation will be able to be stopped, reversed and played.

Add the following to the class.

The attachToViewController() method is passed a reference of the navigation controller which it uses to initiate a pop transition when a gesture occurs. We then set up a gesture recognizer, which will call the handlePanGesture() method when a swipe is made. This checks the gesture recognizer state and does the following at each stage:

  • Began: It sets transitionInProgress to true and initiates a pop navigation.
  • Changed: Here the gesture is in progress, so it determines the percentage of the transition. A 200 point swipe will cause the transition to be 100% complete. It then determines if the transition should complete depending on where the gesture finishes. Here we check if the user swiped to at least half the screen before releasing.
  • Cancelled/Ended: Sets the transitionInProgress to false and cancels the transition if shouldCompleteTransition was set to false or if the gesture was cancelled. Otherwise, the transition is completed.

We used a computed property to determine the completion speed. completionSeed is a UIPercentDrivenInteractiveTransition property that informs the framework how much of the animation remains when a gesture completes. A larger number will make the view controller snap back quicker if the interaction is cancelled.

To use our interaction controller, open ItemsTableViewController.swift and add the following to the class.

Add the following at the beginning of the navigationController(_:animationControllerForOperation:
fromViewController:toViewController:)
function.

This calls the CustomInteractionController’s attachToViewController() method and passes it a reference of the to view controller when it detects a push navigation operation.

Then add the following to the class.

After the framework asks for and gets an animation controller, it asks for an interaction controller using the method above. The above returns an instance of our interaction controller if a transition is in progress.

Run the app and you should see the transition shown.

Custom View Animation #9

Conclusion

We’ve looked at how to create custom view controller transitions and how to make a transition interactive. With these features, developers now have full control over the animations used in their application as it transitions from one view controller to the next and thus can build unique experiences to delight their users. You can download the completed project here.

Note: This tutorial is also available in Chinese. We’re going to support other languages soon. If you want to join our translation team, please contact us.
Tutorial
ARKit Tutorial: Working with 2D Image Recognition
SwiftUI
Using ScrollView to Build an Image Carousel
Tutorial
Building a Simple Barcode Reader App in Swift
  • IDesignIT Kungsbacka

    Thank you for a nice tutorial!

    However you should probably use snapshotting in the CustomNavigationAnimationController as well, because I get a bunch of constraint errors e.g. Unable to simultaneously satisfy constraints, Will attempt to recover by breaking constraint.


    • Joyce Echessa

      Thanks for letting me know.

      I’d made some changes to the project while writing the tutorial and forgot to update the starter project. If you run the completed project, you’ll see that those warnings aren’t there.

      In your project, to get rid of the warnings, delete the horizontal center alignment constraints on the image view and text view in the View Controller scene (they are redundant here anyway, since I’m using trailing and leading space constraints).

      The post will be updated with the correct starter project.


    • Simon Ng

      Simon NgSimon Ng

      Author Reply

      The starter project has been updated. Please redownload it and try again.


  • solingolr

    solingolrsolingolr

    Author Reply

    This is awesome!
    Just one question, can you change the background in the last part (cube animation)?
    Would it be possible to put an image or something?
    Thank you so so much (Ps. I love this blog!!!!)


    • Amir Vinod

      Amir VinodAmir Vinod

      Author Reply

      Hello! Does anyone know how to do this? it looks like it would be really cool.

      Thanks you so much!


  • anish parajuli

    Can i do it without using segues??? I tried presenting view controller from code but the delegate method is not called. ??


    • Balla Niang

      Hello, did you achive this transition stuff without segue ?


  • Siarhei Brazil

    Great tutorial.
    But I was thinking that it is possible to get the same effect on view dismissal using auto layout, without creating snapshot.

    When I have tried using aspect ratio constraint instead of Height restriction, I didn’t get expected results. Why it is like that?


  • flavordaaave

    Awesome tutorial. Just one question. I’m trying to build a modal that does not take over the whole screen and looks like an overlay. So I adjusted the “toViewController.view.frame” to the size I wanted and changed the Presentation of the segue in Storyboard to be “Over Current Context”. When I now dismiss the modal, I get a black screen as soon as the animation finished. I figured out that it’s working if I remove the “transitionContext.completeTransition(!transitionContext.transitionWasCancelled())” from the dismiss controller but then the Main View is not getting active anymore and I can’t click on any item there…


    • Ian Warburton

      Did you find a solution?


      • Farhad Saadatpei

        To make it look like a overlay, change the Y position of your frame:
        toViewController.view.frame = CGRect(x: 0, y: 40, width: finalFrameForViewController.width, height: finalFrameForViewController.height)

        Also adjust the alpha to your need:
        fromViewController.view.alpha = 0.2


  • Alex

    AlexAlex

    Author Reply

    How would i make the background TRANSLUCENT. My tableview is see through but when the animation stops/completes, the background is solid.


  • Christian Bale

    Is this tutorial for objective c and not swift?


  • mehulpatel

    mehulpatelmehulpatel

    Author Reply

    Great tutorial.Thanks for sharing!!!


  • Tolgay

    TolgayTolgay

    Author Reply

    How can I do interactive transitions without navigation controller? Is this possible?


  • Nico

    NicoNico

    Author Reply

    I did a transition manager based on this tutorial and some other. Then I realised it was buggy (the layout is acting weirdly) when entering in the child view controller, changing the orientation and from there, going back to the parent controller. I thought it was only my transition manager then I tried on your project and it did the same. Were you aware of it? Any idea how to fix it?


  • Jordo

    JordoJordo

    Author Reply

    this is awesome! if I wanted to do a custom pop or push with the navigation controller for only a few select Viewcontrollers, how would i do so? because currently all of the push / pops are affected by this code


  • Elliott

    ElliottElliott

    Author Reply

    Great tutorial! Thank you. I ran into one issue though. If you start in one orientation, select an item from the table and transition to the display through the cube animation and then change orientation before transitioning back to the item table, a large portion of the screen goes black. It doesn’t seem to matter whether you start in landscape or portrait. The other transitions do not seem to have this problem.

    Any help in identifying the cause of this behavior would be appreciated.


  • Stack Help

    Stack HelpStack Help

    Author Reply

    can you help me to convert this
    var const = CGFloat(fminf(fmaxf(Float(viewTranslation.x / 200.0), 0.0), 1.0))

    shouldCompleteTransition = const > 0.5

    updateInteractiveTransition(const) into Objective c?


Leave a Reply to Joyce Echessa
Cancel Reply

Shares