Popping into UIViewPropertyAnimator

I spent some time today tinkering with UIViewPropertyAnimator. It's a new class for performing animations in UIKit, just introduced in iOS 10. I've noticed a few things not covered in the WWDC session that I think are quite interesting.

It's Not Pop

Lots of developers (yours truly included) started using Facebook's Pop for some of the most advanced animations. Its API allows us to easily create interruptible, gesture-driven animations. At first sight, UIViewPropertyAnimator seemed like a sherlocked Pop. Turns out, far from it.

Core Animation 101

Let's start by recapping how regular Core Animation-based animations work on iOS. To do that, we'll use a diagram from Advanced Graphics and Animations for iOS Apps session from WWDC 2014 (the whole session is definitely worth watching):

Animation: three-stage process (source: apple.com)

There are three steps to each Core Animation-based animation:

  1. The application creates an animation.
  2. Layout, Display, Prepare and Commit phases are performed once before the animation starts. At this time the model layer is updated.
  3. The animation itself is rendered in a separate process on the render server using the presentation layer.

Pop's Approach

With Pop, bulk of the work is done on the application side on each frame directly on the model layer:

  1. Internal data structures for an animation are created by Pop. CADisplayLink is started.
  2. Layout, Display, Prepare and Commit phases are performed on each frame. Values of properties are interpolated by Pop based on the progress, timing curve, and so on.
  3. Render server renders a single frame.

Does the difference between these two ways of driving animations matter in real life? In my experience, it often does. Especially when we're working with complex view hierarchies (one example: collection views). In those cases, even layout can be too expensive. The tradeoff is clear:

  • Core Animation-based animation blocks before it starts running but then it runs smoothly
  • Pop-based animation starts immediately but can drop frames because it has to do more work on each frame

What Makes UIViewPropertyAnimator Better

So, how does UIViewPropertyAnimator fit in here? It brings Pop's flexibility to Core Animation. As an outside developer, not having access to UIKit and Core Animation sources, I used the good ol' debugger to build an understanding of UIViewPropertyAnimator inner workings:

  1. After UIViewPropertyAnimator instance is first created, for example with:

    let originChange = UIViewPropertyAnimator(duration: 3, curve: .easeIn) {
        myView.frame.origin = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.midY)
    }
    

    there's no CAAnimation instance associated with the layer (checked with CALayer.animationKeys()).

  2. When the animator is started, either by calling startAnimation, startAnimation(afterDelay:) or by changing fractionComplete value, the system creates CAAnimation instance and uses it for the given layer.

  3. The animation isn't destroyed until the animator isn't stopped/completed. So, it can be present even when nothing moves on the screen (e.g. in the case when we drive it manually with fractionComplete property).

What does this mean? It means that the animation itself is performed exactly the same as it was before iOS 10. What's new is that we're given the power to move through the animation's progress however we want to.

Well-Architected API

This is a completely new UIKit API and it's clear that it was influenced by Swift. We've got clearly separated responsibilities, protocols where they should be and real objects instead of often too-constraining enums (see UICubicTimingParameters). I'm really happy to see Apple taking this direction, at least before we see a complete UIKit replacement (we will someday, right?).

UIViewPropertyAnimator API diagram (source: apple.com)

It's the Way Forward

Comments in the API seem to suggest that UIViewPropertyAnimator is going to be a new recommended way of performing animations in UIKit. It's going to put the well known animate(withDuration... family of methods behind.

UIViewPropertyAnimator API comments

It's not even the first time the recommended API changes. Back in iOS 4 classic begin/commit methods were replaced with animate(withDuration... methods themselves.

Conclusion

I recommend checking UIViewPropertyAnimator and playing with it. It's the future. And it's really well done.