{"id":5217,"date":"2020-01-13T00:00:00","date_gmt":"2020-01-13T00:00:00","guid":{"rendered":"https:\/\/hedgehoglab.com\/ios-transition-animations-the-proper-way-to-do-it\/"},"modified":"2023-11-06T09:54:34","modified_gmt":"2023-11-06T08:54:34","slug":"ios-transition-animations-the-proper-way-to-do-it","status":"publish","type":"post","link":"https:\/\/hedgehoglab.com\/ios-transition-animations-the-proper-way-to-do-it\/","title":{"rendered":"iOS Transition Animations: The proper way to do it"},"content":{"rendered":"\n
Did you know (I only realised fairly recently!) that Apple\u201a from iOS 7 onward\u201a have provided UIKit APIs to apply custom animations to transitions in a pretty neat way. They allow us to define animation classes that can be applied to both push\/pop transitions on a navigation stack\u201a as well as presenting animations on modal popup transitions. <\/p>\n\n\n\n
The good news is that these are highly reusable too\u201a as the way in which they are defined (by implementing a series of methods within a class which conforms to an animation protocol) allows us to easily integrate them into other parts of an app or even an entirely different app. <\/p>\n\n\n\n
Before we look at how these animation classes are implemented\u201a we need to understand how we can include them in our apps. <\/p>\n\n\n\n
Note: Updated June 2021. There was a supplementary Xcode project that came with this blog. Most of the key parts are explained in this document however the Xcode project cannot be accessed through this web page. Please get in touch if you would like further information on the project. <\/i><\/p>\n\n\n\nNavigation Stack Animations and Modal Animations<\/h3>\n\n\n\n
As mentioned above\u201a our custom animations can be applied to both modal styled transitions and navigation stack animations. The way in which we apply the animations to these mechanisms is quite different so we\u2019ll need to focus on them separately.<\/p>\n\n\n\n
We\u2019ll start with the Navigation Stack mechanism first\u201a as this one is slightly more straightforward.<\/p>\n\n\n\n
In the CustomAnimation<\/i> Demo App attached to this tutorial\u201a locate the PresentingViewController.swift<\/i> file. This is the ViewController for our screen which performs the segues that will trigger our custom animations.<\/p>\n\n\n\n
The first step is to make the PresentingViewController <\/i>class conform to the UINavigationControllerDelegate.<\/i><\/p>\n\n\n\n
This is because\u201a in order for our custom animations to be considered by the system\u201a we need to implement a method belonging to the UINavigationControllerDelegate <\/i>protocol. The purpose of this method is to create an animation object (which we will come to in a little while) and return the object back to the system. Before we take a look at this method\u201a we mustn\u2019t forget to assign a delegate object to the navigation controller\u2019s delegate property. We do this in the viewDidLoad <\/i>method\u201a and the delegate is our PresentingViewController.<\/i><\/p>\n\n\n\n Now we can implement the necessary method to provide our animation object. Before that though\u201a a quick\u201a simplified look at what is going on under the hood when we transition from one view controller to another.<\/p>\n\n\n\n At some point when the transition occurs\u201a our view controller\u2019s navigation controller checks to see if the source view controller (in this case\u201a our PresentingViewController<\/i>) conforms to the UINavigationControllerDelegate <\/i>protocol. If it does\u201a it will invoke the delegate method which we will implement in a moment (the one that returns our animations object)\u201a otherwise the system will perform the default animations.<\/p>\n\n\n\n So let\u2019s implement our UINavigationControllerDelegate<\/i> delegate method.<\/p>\n\n\n\n There is quite a bit going on here so let\u2019s first look at the parameters that are being passed in to the method. The navigationController<\/i> object is simply the navigation controller our view controller belongs to\u201a and is the owner of the our UINavigationControllerDelegate <\/i>object. The operation <\/i>object is an enum instance which tells us whether we are pushing or popping on the navigation stack – very useful for when we build our animation. Finally\u201a the fromVC<\/i> and toVC<\/i> are the source and destination view controllers that make up our transition.<\/p>\n\n\n\n You\u2019ll notice that the delegate method return type\u201a the animation object\u201a is optional. If you return nil\u201a the navigation controller will use the default system animations. This is useful if you want to conditionally determine which animations to use depending on the transition\u2019s segue.<\/p>\n\n\n\n Now that we understand how to tell the system to use a custom animation\u201a we can look at how we build the animations themselves. <\/p>\n\n\n\n In our method implementation there is an instance of RevealFromFrameAnimator <\/i>created. This is our custom object that conforms to the UIViewControllerAnimatedTransitioning <\/i>delegate protocol. This is the object that the system will use to build and execute our custom animation. Notice that there are a couple of properties set on the object. These are properties we have implemented on our animation class to help build up the various aspects of the animation. To understand these\u201a we will need to take a look at the RevealFromFrameAnimator <\/i>class.<\/p>\n\n\n\n The first thing to notice is that our RevealFromFrameAnimator <\/i>class implements the UIViewControllerAnimatedTransitioning <\/i>protocol like so:<\/p>\n\n\n\n Also note that the class inherits from NSObject. The class in which we implement our animation methods can be of any type. However\u201a if you plan to make your animation classes interchangeable between projects\u201a it does make sense to implement them in a class which has a sole purpose of providing only transition animations.<\/p>\n\n\n\n As a result of our class confirming to the UIViewControllerAnimatedTransitioning <\/i>protocol\u201a we need to implement a few methods. <\/p>\n\n\n\n The first of these is the transitionDuration <\/i>method. The purpose of this method is to inform the system the total duration of the transition in seconds. The system uses this value to synchronise any other system animations that occur alongside your own. For example\u201a the navigation bar animation that occurs during a transition will also use this value. There is a single parameter (transitionContext<\/i>) on this method that doesn\u2019t get used here. It will be discussed in the next method as it is used there. <\/p>\n\n\n\n The second and final method of note\u201a is the animateTransition <\/i>method. This is the method where we define our animations and how they affect the various views involved. The parameter mentioned above\u201a the transitionContext<\/i>\u201a essentially contains all of the information associated with the transition. The three crucial items we obtain from this object are the containerView<\/i>\u201a the from<\/i> View and the to<\/i> View.<\/p>\n\n\n\n The containerView<\/i> is the parent view of all views that are involved in the transition including the from<\/i> view and the to<\/i> view. The system creates this view for you and it will also add our from<\/i> view to the container view (though do note that it only does this on the push and not the pop). It is then our job to add the to<\/i> view to the container view. At this point\u201a our transition view hierarchy is all set up and we are ready to animate.<\/p>\n\n\n\n You\u2019ll notice that in the method\u201a we are doing some slightly different variable assignments depending on the instance variable forward<\/i>. This is a custom property on our animation class whose value is set from outside the class by our presenting view controller. It tells our animation class internally whether or not we are pushing or popping. This is also important because we need to know whether or not we are adding the presenting view ourselves\u201a or letting the system add it as mentioned above.<\/p>\n\n\n\nclass PresentingViewController: UIViewController\u201a UINavigationControllerDelegate\u201a UIViewControllerTransitioningDelegate { <\/code><\/p>\n\n\n\n
self.navigationController?.delegate = self <\/code><\/p>\n\n\n\n
func navigationController(_ navigationController: UINavigationController\u201a animationControllerFor operation: UINavigationControllerOperation\u201a from fromVC: UIViewController\u201a to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { transition.originFrame = self.pushButton.frame transition.forward = (operation == .push) return transition}<\/code><\/p>\n\n\n\n
class RevealFromFrameAnimator: NSObject\u201a UIViewControllerAnimatedTransitioning\u201a CAAnimationDelegate { <\/code><\/p>\n\n\n\n