The code

Finally we can dive into the code and understand how this transition can be done. I will post code blocks gradually and explain every part.

Disclaimer

I simplify some parts of the code related to views hierarchy.

This is the beginning of animateTransition(using transitionContext:) method. Here we are trying to get access to controllers which are involved in this transition. As you can see this operation is wrapped with guard and if at least one of our checks fails we interrupt the transition. Please, pay attention to transitionContext.completeTransition(true) call. It allows us to fallback gracefully and complete the transition without animation. Otherwise if we use only return in else branch the app will freeze and become irresponsive, and the only way we will have is to relaunch the app.

Here we add the view of the destination view controller to the containerView and send it to the bottom of the view hierarchy. We did it on purpose because we will show it gradually and don’t need our user to see the whole view during the transition. Bear in mind that we add view of toNavigationViewController and not the destination view controller itself. Because otherwise it will lead to unpredictable behaviour and can cause a lot of mysterious bugs for simple push or pop actions of navigation controller.

Next we need to make all the required snapshots and gracefully fallback if something goes wrong. For our transition we need these snapshots:

textField — snapshot of the card’s text field

— snapshot of the card’s text field cardStepsView — snapshot of the steps view on the card

— snapshot of the steps view on the card titleLabel — snapshot of the small title label on the card

— snapshot of the small title label on the card destinationBackgroundView — snapshot of the background view with gradient in the destination view controller

— snapshot of the background view with gradient in the destination view controller destinationStepsView — snapshot of the steps view of the destination view controller. If you remember, the original steps on the card are cut and we want them to be uncut by the end of the animation. So we will combine them during the animation and nobody will notice that there is any kind of trick here. I bet you didn’t notice anything suspicious with the steps until reading that explanation 😉

— snapshot of the steps view of the destination view controller. If you remember, the original steps on the card are cut and we want them to be uncut by the end of the animation. So we will combine them during the animation and nobody will notice that there is any kind of trick here. I bet you didn’t notice anything suspicious with the steps until reading that explanation 😉 destinationNavigationBar — snapshot of the container with navigation elements such as a back arrow

— snapshot of the container with navigation elements such as a back arrow destinationControllersContainer — snapshot of the container which contains view controller with the search field and the countries list

Brief summary: We just end up the preparation to the animation. This is a common part of any animation. Find controllers, place views in a container and prepare all the snapshots and the views we need.

This part is pretty simple — we just hide the original card view and take it’s frame in window’s coordinate system. Let me show you frameOfViewInWindowsCoordinateSystem() function.

Here we are checking if the view has a super view. If it doesn’t, print warning and return an original frame. If the view is ok and takes its place in the views hierarchy then we convert its frame into the window’s coordinate system by passing nil to to parameter.

Next we prepare destinationBackgroundView — we take its original frame and make it look like a cardView . Also we take its original frame so we can move it back during animation.

And set all frames and properties for the rest of card’s inner views snapshots.

Brief summary: Now we end up preparation of the “fake card”. We have all the snapshots that allow us to imitate it and transform it safely without affecting the real card view.

Next we just prepare navigation elements so it can smoothly fade in.

Here we are tightly close to the trickiest part of the animation. We place a controller’s container of the destination view controller in the origin of the card’s text field. But before we dive in let’s just add everything into the transition’s container.

For adding views this way I use the following UIView extension

Brief summary: Now we’re almost done our preparations before transition. All the snapshots are in place and ready to be animated. The only thing left is masking.

This part of code looks pretty simple — we just create instances of CAShapeLayer as masks and set the path s with rounded corners. Let’s pretend that you didn’t notice that path is set with a function that I didn’t explain. I’ll do it later, I promise.

The animation begins! As you already know the layer’s properties should be animated with CAAnimation . I create a helper function for it.

I won’t explain it step by step because there are a lot of articles and explanations of layers animations. But the main idea of this method to animate layer’s path from the original value to the one we pass.