Getting Started With iOS Animation (Part 3)
By: Quang Ngo
Tags: iOS, Animation, Swift, Programming, Mobile

Getting Started With iOS Animation (Part 3)

How to animate transitions between view controllers

Photo by Nathan Anderson on Unsplash

Welcome the third and final part of our Animation series. In this story, we will discuss how to implement the animation when transiting between view controllers.

You can also find parts one and two here:

Note: I recommend starting where we left at the last part, so you can download the project here. Otherwise, you can still follow this tutorial by creating two view controllers.

At this point, we have LoginViewController and GiftViewController :

The animation on the screen is great, but the transition between theses two views seems ordinary, so we will improve it by implementing some juicy animation. Here’s the final expected result:

Actually, we will go the extra mile by adding a controllable dismiss transition, which means by swiping from the left edge of the screen, the animation can move forward and backward a finger drag:

You may see the code seems intimidating at first, but actually the principle is simple. The transition animation can be done by creating a class conform with UIViewControllerAnimatedTransitioning . Then, we inject it in the UIViewControllerTransitioningDelegate of a view controller.

Tip: Animation parameters were found through trial and error. We often need to monitor closely each move we make, so we need to make that move slower. The simulator provides a great function for slow motion: go to Debug and select Slow Animations .

Present transition animation

We begin with the Present transition. Here are the animation steps:

Rotating the “Open” button 90 degrees along the y-axis clockwise.

Display the gift snapshot view at the place and with the size of “Open” button, but the initial place is 90 degrees anti-clockwise on the y-axis. It’s because it helps the button rotating looks continuous.

Then we rotate it along y-axis clockwise 90 degrees.

Zoom the gift view out to fill the screen.

Here’s the code:

The most important part is taking a snapshot of the destination view:

let snapshot = toVC.view.snapshotView(afterScreenUpdates: true)

And here the code of each step:

Rotating the “open” button 90 degrees along the y-axis clockwise:

UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1/4) {

fromVC.loginButton.layer.transform = CATransform3DMakeRotation(CGFloat(-Double.pi / 2), 0.0, 1.0, 0.0)

}

By using UIView.addKeyframe , we can specify the animation’s duration of each step relative to the whole animation’s duration. Here we want the rotation time of the “Open” button take a quarter of the whole duration.

Rotating the Snapshot destination view 90 degrees:

But first, we must initialize it at 90 degrees anti-clockwise on the y-axis. We do this before the whole animation.

snapshot.layer.transform = CATransform3DMakeRotation(CGFloat(Double.pi / 2), 0.0, 1.0, 0.0)

And then rotate it after rotating the “Open” button:

UIView.addKeyframe(withRelativeStartTime: 1/4, relativeDuration: 1/4) {

snapshot.layer.transform = CATransform3DMakeRotation(0.0, 0.0, 1.0, 0.0)

}

On the final step, we zoom out the snapshot to match the final destination view:

UIView.addKeyframe(withRelativeStartTime: 2/4, relativeDuration: 1/2) {

snapshot.frame = finalFrame

}

At the completion of this whole animation, we do the cleanup:

Display the destination view,

Remove the snapshot, and

Reset the “Open” button position.

And the present animation was born. Now, all we have to do is to wire it to our view controllers. So when the app prepares the segue, we must specify that we want to use our custom transition (by specifying transitioningDelegate ) instead of the default one:

And then feed our animation instance in the right delegate function:

And it’s done. Run the app and enjoy the first animation.

Dismiss transition animation

Since we successfully created the present transition, we will just do the same thing to dismiss one.

Here’s the code for dismissing animation:

Just ignore SwipeInteractiveTransition for now. We talk about it later.

Here we’ll find the same pattern of code that we wrote in Present animation, just notice that .to and .from view controller was inverse.

We will create the dismiss view in three steps:

Zoom out the current view to the size of the destination button:

UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 1/2) {

snapshot.frame = self.destinationFrame

}

Rotate it 90 degrees on the y-axis: We also rotate the “Open” button by 90 degrees so at this moment we won’t see the “Open” text:

UIView.addKeyframe(withRelativeStartTime: 1/2, relativeDuration: 1/4) {

snapshot.layer.transform = CATransform3DMakeRotation(CGFloat(Double.pi/2), 0.0, 1.0, 0.0)

toVC.loginButton.layer.transform = CATransform3DMakeRotation(CGFloat(Double.pi/2), 0.0, 1.0, 0.0)

}

Finally, rotate the “Open” button back to the right position:

UIView.addKeyframe(withRelativeStartTime: 3/4, relativeDuration: 1/4) {

toVC.loginButton.layer.transform = CATransform3DIdentity

}

Now that we have the animation created, we just need to wire it in the UIViewControllerTransitioningDelegate :

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {

return DismissViewControllerAnimation(destinationFrame: loginButton.frame)

}

Cool, now we finished all the animations. Run the app and enjoy the magic!

Control the animation

iOS makes it really easy to give user interaction with the animation. And it works the same way as we did with these two animations. First, we create a subclass of UIPercentDrivenInteractiveTransition and then inject it in the function interactionControllerForDismissal of our UIViewControllerTransitioningDelegate . The class UIPercentDrivenInteractiveTransition provides three functions:

update() : Tells us how much of the transition was complete. 0.0 is the beginning of transition and 1.0 is the end of the transition.

: Tells us how much of the transition was complete. 0.0 is the beginning of transition and 1.0 is the end of the transition. cancel() : Cancel the transition. In our case, it will cancel the dismiss process.

: Cancel the transition. In our case, it will cancel the dismiss process. finish() : Finish the transition. In our case, it will dismiss the Gift view and show the Login view.

We usually use this interactive transition combined with gesture recognizer, since it gives user the ability to control the transition. Here’s the code of this subclass:

We will add UIScreenEdgePanGestureRecognizer to the gift view. This recognizer can detect a dragging from the edge of the screen. Then, based on how much the user is dragging, we use update to control the transition. When the gesture ends, we check if the progress is more than half, then we go forward and finish the dismissal; otherwise, we cancel it.

We can instantiate this controller in GiftViewController :

private(set) var swipeTransition: SwipeInteractiveTransition? override func viewDidLoad() {

super.viewDidLoad()

setupUI()

swipeTransition = SwipeInteractiveTransition(viewController: self)

}

And then inject it in a function of UIViewControllerTransitioningDelegate . Here’s the final version of this delegate:

Awesome! We finished it. Now it’s time to admire our stunning transition animation!