In Part 1, we covered the basic animation cases using UIView.animate. But when it comes to advanced animations, we have to use Core Animation.

What’s the difference between UIView & Core Animation animations?

UIView.animate is a convenient helper to create animations that will be using Core Animation. Because UIView.animate also updates the model values along with the animation you might end up with weird behaviors.
If you decide to animate a view’s frame using UIView.animate you might run into issues with rotations for example. As you have seen in Part 1 we avoided animating a frame or a constraint directly, and we were just playing with the layer transformation. The view’s frame was restored after the animated transform was completed :

The view frame is restored after a CGAffineTransform

When you want to play with advanced animations it is better to directly interact with CALayer. It is a bit more difficult (documentation might be incomplete in certain cases) but it gives you better control.

After all, Core Animation was shown publicly the first time WWDC 2006 and according to rumors, the framework has been written by a single developer.

CABasicAnimation

One of my favorite apps of all time is Flipboard. I instantly fell in love with the page flip animation in that app. Let’s try to reproduce that!

Start with a view that you would like to animate. I went for a simple image, badge, title label, and subtitle label one, close to a Flipboard article tile :

Test view for our animation

Enough UI for today, now let’s have a look at the animation side:

Let’s go into detail about what is happening in the code above.

  • We create a CABasicAnimation that should have a scope unique keyPath (important if running multiple animations at the same time)
  • We create a generic CATransform3D. Identity is just the CATransform default matrix : [1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]
  • The CATransform matrix ranges from id m11 to id m44. Since we want to create a generic/plain 3D rotation, m34 is what we are looking for (3rd row and 4th column in our matrix). The first line (m11 to m14) refers to the x-axis. The second line to the y-axis and the third to the z-axis. The fourth line repositions all other axes. The translation vector t = rotation(angle, x, y, z) we use in CATransform3DRotate will take in the angle defined by this matrix.
    Try changing the values in the matrix to experiment until you reach the expected result. A decimal matrix value ranges from -1 to 1.
  • We set our animation toValue property to a CATransform3DRotate. The first parameter is a vector: we pass it the CATransform3D we created above (transform). Then we have to set the angle of the rotation and set the X, Y, Z axis. We are looking for a 90 degrees horizontal, left to the right rotation. So we set the angle to CGFloat(90 * Double.pi / 180.0). Then we only pass 1 to the Y axis to make the 3D rotation horizontal only.
  • We set the animation duration. See CAAnimation and CAPropertyAnimation for other settings. CASpringAnimation inherits from CABasicAnimation and might offer you better options
  • We then add the animation to our view layer

Here is the output:

a CABasicAnimation using CATransform3DRotate transform

As you can see we are not there yet. We have some issues to work on:

  • the layer transform is getting reset after the animation is completed. We want to commit this state.
  • the rotation anchor is in the middle of our view making our animation different from the “Flipboard” type one. We want to feel it like a page turn.

Let’s tackle each problem separately.

CAAnimation Completion

Because we are using CABasicAnimation the model does not get updated (vs. UIView.animate). We need to set up a completion block to enforce the transformation at the end of our animation. For that we have two solutions on Swift 4:

  • Use a CATransaction with a completion block
  • Use CAAnimationDelegate

Both are doing the same thing, it will come to your preference. If I am combining more than one CAAnimation, I usually go with a CATransaction block. I also prefer the block scope to make it easier for peers to understand what’s going on.

The CATransaction solution

It is pretty simple to set up. Starting from the code above, add CATransaction.begin() before performing any animations. Then set your completion block. Wrap this up by calling CATransaction.commit()

Note: you will have to specify animation.fromValue for CATransaction to work properly:

animation.fromValue = CATransform3DRotate(transform, 0, 0, 1, 0)

The CAAnimationDelegate solution

Start by setting your animation’s delegate to self :

animation.delegate = self

Then implement the delegate to restore your transformation :

You might want to check your CAAnimation key if you are expecting multiple animations to call the delegate.

That fixes our reset problem and commits the change to the model:

After committing the transform permanently in the completion

Animation anchor point

Now let’s fix our second issue. To make our animation feel like a page flip, we need to set up the anchor point on the right side of the view instead of the center. If we directly set our view layer’s anchor point it will change the position/frame of our view. To counter that we need to do a bit of calculation.
I borrowed Paul Hudson’s solution :

All we have left to do is to add:

myView.setAnchorPoint(CGPoint(x: 1, y: 0.5))

This will set the anchor point of our view to the right middle side.
The final version of our animation function:

The final result 🎉:

After setting the anchor point on the right side

Beware that this solution is not great if you are using autolayout. You will have to subclass your view and enforce your anchor point settings in layoutSubviews().

What’s next?

The CoreAnimation framework has a lot to offer in terms of possibilities. We barely scratched the surface in this Flip animation exploration.

You can create gradient and shadow animations, play with more type of affine and 3D animations, etc…

As always, thanks for reading and let me know your thoughts below! 🖖🏼

This story is published in Noteworthy, where 10,000+ readers come every day to learn about the people & ideas shaping the products we love.

Follow our publication to see more product & design stories featured by the Journal team.

--

--