How I re-created this animation through coding

To get started, let me break down the animation into small pieces and explain how I implemented it.

Initial State

The animation needs 2 views, the aboveView and the belowView . We notice that the aboveView is always at its place and the belowView is always higher and a little bit smaller.

The aboveView will always return the view that is closer to our eyes based on the isFirstViewAbove flag while the belowView will return the view further from our eyes.

We can achieve the initial state by making some default CGAffineTransforms, one for scaling and another one for translation (moving it up by an arbitrary value of -30).

Now, all we have to do is to implement the initialAnimationState function:

We need to modify each view’s layer zPosition.

It is a value [-greatestFiniteMagnitude, greatestFiniteMagnitude] which changes the front to back ordering of the onscreen layers. So a higher value will place the layer closer to our eyes.

The frontView will have the zPosition = .above which is 2 and the behindView will have the zPosition = .below which is 1 and concatenate the scaling transform + translation transform

The user starts dragging

We will need an UIPanGestureRecognizer so that we can manipulate and move the view.

Here we notice there are 4 states:

Only after an amount of drag, does the alpha start fading. We will say that only after the translation.y is greater than 20.

1. Fade

Add this to the .began, .changed cases:

Now we have our aboveView fading its alpha and move based on the translation.y (how much the user dragged on the yAxis), but the view doesn’t return to its initial position when the pan gesture ends.

2. When the user ends dragging we should make the aboveView return to its initial position.

2. Users ends dragging

We just apply a transform = .identity on the aboveView and animate its alpha back to 1.

3. The user drags down and after a dismissalOffset (we can choose an arbitrary value of 80) the aboveView changes its zIndex and transforms concatenating the scaling transform + the translation transform

3. Drag down

4. The user drags up and after a dismissalOffset the aboveView changes its zIndex . We notice that it comes from the bottom of the screen to its initial state.

I find this kind of weird and it really makes no sense, so I will implement what I consider to be better UX: coming naturally from where the user dragged more than the dismissalOffset .

4. Drag up

After we animate the alpha in .began, .changed cases, add this:

As you may notice, we disable the panGesture . When the currentOffset > dismissalOffset , the .ended case won’t get called.

All that’s left to do is to implement the handleTransition method:

AboveView:

we animate the alpha back to 1

back to 1 we change its zIndex = .below

and concatenate the scale and the translation transforms

BelowView

we change its zIndex = .above

and transform it to .identity

In the completion block we toggle() the isFirstViewAbove flag and re-enable the panGesture , and our animation is done.

Final Result

My implementation playing SICKO MODE 🔥

Trying to re-create an animation has always been on my to-do list.

Thanks to ajlkn and his tweet I’ve managed to break the ice between me and my first Medium post.

If you have any questions or want to give me feedback, I’m always available on Twitter. If you’d like to see more of my work, check out my website and github.

Bonus

The full source code is available on GitHub supporting drag on yAxis and xAxis or on both of them:

____

Andrei Olteanu is an iOS developer at Halcyon Mobile, a full-service mobile app design and development agency that creates award-winning mobile products for bold startups and brands.