High-end transitioning in iOS

Dino Bozic
Azikus

--

“Wow, how does Instagram make their screens animate like that?”
“Amazing animations, I wish I could do that.”
— an iOS developer who hasn’t read this article

In this article, a few techniques that are making a difference for those high-end projects and applications will be presented. An important mention is that we are doing everything programmatically using UIKit, without the usage of storyboard/.xib files.

What exactly constitutes a high-end application? The answer usually lies in additional attention to detail and perfecting every user interaction inside the application. High-end applications usually contain a lot of “natural” user interactions with incredible smoothness and fluidness. The application should pay attention to “how it feels”. That’s why, for example, Instagram is one of the most popular apps today.

Applications that are considered to be top-notch use cutting-edge technology when it comes to the tools used in their development. Alongside advanced technological techniques, there is a delicate sense of design to ensure the best possible application “feel”.

Upper interaction, taken from Instagram, is the one that is going to be reproduced and explained in this article. The general idea with this interaction is how the flow feels unified — two screens share an image of the selected item. Additionally, we have terrific feedback on our finger moving. The screen follows our finger and dismisses depending on our movement rather naturally. Instagram uses this interaction both on the shop screen and on the user profiles screen to generate the selected item details screen.

Animation implementation

These animations require a pretty complex set of techniques to be implemented. It takes quite a bit of time for it to be perfect and detailed to the wanted behavior.

Our transitioning example consists of implementing animations for a screen presenting and a dismissal with a gesture in-between which decides if the screen should be dismissed based on the user’s behavior. The screens share an image that resizes accordingly to each screen’s needs.

The initial step of the animation is to implement the presentation. The wanted behavior includes that the presentation and the next screen involve the exact image we tapped on. That is going to be done by implementing a custom presentation animation from the starting screen to the one with an enlarged image and details. Similar to this technique, we will also implement the dismissal of the screen which will return the image to its previous location. To complete the screen dismissal behavior, we will also add a gesture which is taking care of the user’s finger interaction.

Transitioning

Usually, every iOS project consists of multiple screens. Those screens need to be shown somehow, right? Well, in iOS screens are shown by either pushing a ViewController on the stack or by presenting a ViewController on top of the parent one.

Pushing and popping ViewControllers usually notes a flow inside an application. A flow needs to have its Navigation controller, which as its name explains, handles navigating screens. Pushing is the action of showing a screen — putting a screen on top of the stack. Popping a ViewController means hiding a screen, removing it from the top. Basic examples are shown below.

navigationController?.pushViewController(ShowingViewController(), animated: true)navigationController?.popViewController(animated: true)

Another way of showing a ViewController is by modally presenting it. It is usually used when you want to interrupt the current flow by an action. Modal screens are widely used for showing error and info messages. Also, they can start a new flow, but by preserving a current flow in the background. Present action shows the modal ViewController while a dismiss action hides the top presented ViewController.

present(UIViewController(), animated: true, completion: nil)dismiss(animated: true, completion: nil)

There are various ways of exploiting native functions for navigation and those methods are thoroughly covered in blogs and StackOverflow.

Transitioning delegation

https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate

Apple provided a way to enable custom animation creation by adding a transitioning delegate to appropriate ViewControllers. That is our first way of creating a custom high-end transitioning behavior between screens. There are two protocols that are available: UIViewControllerAnimatedTransitioning and UIViewControllerInteractiveTransitioning. Animated transitioning is used for basic operations like presenting and dismissing while interactive transitioning is used to implement the behavior of updating the animation while happening. Alongside implementing one of the transitioning delegates, a ViewController must also conform to UIViewControllerTransitioningDelegate. This ViewController, as shown in the code below sets the animated transitioning delegate to itself for the present and dismiss actions.

The next task is the conformation to the UIViewControllerAnimatedTransitioning delegate. This conformation determines the duration of the animation and sets the methods responsible for handling the presenting and dismissal animation. Methods are selected based on the transition context which holds the necessary data about the animation.

While instancing our presented view controller we have to set the modalPresentationStyle property to custom which denotes that we will have a custom handle for its presentation. Next to that, we have to set the transitioning delegate of a newly created ViewController to its presenting one. Of course, we have to note that we are using the animated presentation.

Now, we have all our transitioning delegates set and we are ready to start implementing our custom animation. As we see from the .gif with our finished animation at the top, we are using the same image on both the presenting and the presented ViewControllers — which is exactly the main piece in our animation puzzle.

The definition of our animatePresent function which handles this screen showing includes the transition context, the presented ViewController, and UI elements that are used in the animation. Here we have an imageView which we are transitioning to the presented screen, sizeView which helps us get the frame of the shown image, and dimmingView which produces a shadow on the parent screen.

To start with the animation, we should prepare our “canvas”. Generally, that means we have to use additional views that are exclusively used in the animation process. Canvas creation starts by defining the global transition containerView — the containerView of the transition context. This view will hold all of the necessary subviews used in the animation. We should be careful and hide our presented screen until it is time for it to become visible. That is being done by setting the alpha to 0 for its main view.

The next part is the creation of the views we will use in our animation. The animation will have an image transforming from its current location and size to the new one defined on the new screen. Additional attention should also be granted to animating the rest of the new screen. The rest also appears from the starting image enlarging and moving downwards as the animation progresses. In the code snippet above, we have an initialization of those elements with its frame set to be the same as on the starting screen.

The final part of presenting the screen is the animation. Firstly, we are enlarging the image and relocating it simultaneously. That is being done by animating the imageView’s frame to the one on the new screen. Alongside the image, we are resizing the frame of the contentView to acquire the size of the newly made screen. After the size animation finishes, the key elements should be set in place and then we can animate showing the rest of the presenting screen.
Finally, we are removing temporary views from the container views, adding the dimming view to the presenting screen, and completing the transition. With that, our custom presenting animation is finished.

Next up, we have the dismissal of our presented screen. The very noticeable thing is that our screen moves with our behavior before its being dismissed. That’s gesture handling which will be explained later on. The current focus is the dismissal animation.

The dismissal animation implementation has a similar logic. As with the presenting, dismissal also requires the same definition with UI elements that participate in the animation. In this instance, we are initializing a new imageView element which will transition an image from the presenting screen to its parent. We are taking the current frame of the imageView and converting the frame to the sizeView in the parent screen so we “return” the image in its place. With the image been set in its place, the next thing to do is remove the mocked imageView from our views and complete the transition in the transition context.

Gesture handling

https://developer.apple.com/documentation/uikit/uigesturerecognizer

Gesture recognizers are the object used to capture and handle the user’s input in the applications. Apple’s gesture support includes taps, swipes, pinches, and others.

For our purpose, we will use PanGestureRecognizer which is used to capture and handle the dragging of the user’s finger over the screen.

The initial important thing is view hierarchy. We should use an additional containerView on top of the ViewController’s default view to enable this kind of animation. This enables us to “move” the containerView around the screen with its content on it and maintain the view of the top ViewController intact.

The handle of our movement starts with adding our pan gesture to the containerView and initializing a function that will run after a gesture trigger. As it could be seen from the snippet, two variables are prepared — view which is being dragged by the pan gesture and general translation in the superView. The rest of the handle consists of two parts: the change handle and the ending handle.

Change handle begins with determining the starting point of the translation. This case right here enables the gesture only to be handled when the drag is going to the right. According to that, we are saving the initial and every other subsequent transition so we can precisely determine the data about our next transition. The most crucial part of the gesture animation is the next part — to determine the translation of the containerView and to scale it. The translation is done by setting its center with the desired offset. In the snippet, it is done by offsetting the center by translation value multiplied by a constant of 0.35. To determine a wanted scale, we conducted a small algorithm that calculates the scale and then is applied using the CGAffineTransform. With those methods we get the screen moving and resizing smoothly.

This snippet contains an ending phase of our gesture. This chunk of code fires when the pan gesture ends — that can happen when the user reaches the edge of the phone or he lifts his finger from the screen. Based on the velocity of the user’s finger, the algorithm determines whether the screen should dismiss or return to its initial state.

Room for upgrades

Just as any other software development element we can also enhance the behavior of explained techniques. The upgrades might consist of remodeling the animations — using more views or more complex animation patterns. Space for imagination is unlimited so any element transition with explained patterns is possible. It is worth mentioning that one can also try implementing interactive transitions — usually used in push/pop transitions.

Those interactive transitions as their name suggests are consumed while the user is interacting with the application. That means that screens are showing or hiding parts of their content while the user is deciding his next action with his gesture.

Last call

Combining mentioned techniques makes transforms a basic screen transition into a high-class one. These techniques are used by top-notch applications with unlimited budgets. Both the discovery and the implementation of those transitions are rather costly and complicated actions. They are, however mandatory for a supreme iOS developer if he wants to compete with the big-game players. Complex and costly actions are usually the elements that make the difference in the end considering the application’s overall performance and user experience.

Dino is a valuable member of our iOS team.
At Azikus, we design and develop top-notch mobile and web apps.
We are a bunch of experienced developers and designers who like to share knowledge, always staying up to date with the latest and newest technologies.
To find out more about what we do, feel free to check our
website.

--

--