Think & Build

Icon

Quick Guide: Animations with UIViewPropertyAnimator

With iOS 10 came a bunch of new interesting features, like the UIViewPropertyAnimator, a brand new class that improves animation handling.
The view property animator completely changes the flow that we are used to, adding a finer control over the animations logic.

A simple animation

Let’s see how to build a simple animation to change the center property of a view.

let animator = UIViewPropertyAnimator(duration: 1.0, curve: .easeOut){
	AView.center = finalPoint
}
animator.startAnimation()

There are at least 3 interesting things to note:
1) The animation is defined through a closure, really similarly to the UIView animation helpers “UIView.animation(duration:…)”.
2) An object, the animator, is returned.
3) The animation is not started immediately, but is triggered with the startAnimation() function.

Animation state

The major changes in the way we animate an element are related to the fact that with a property animator comes a fully state machine logic. Through the UIViewAnimating protocol are implemented features to manage the state of the animation in a simple and clear way, implemented by functions like startAnimation, pauseAnimation and stopAnimation. Calling those functions we update the state value, making it switch between active, inactive and stopped.

The animation state is active when the animation is started or paused, it is inactive when it has been just initialized and not started or, when it’s completed. It is better to clarify that there is a little difference between inactive and stopped. When the animation completes after a stop commands or it completes, the state becomes stopped, internally the animators calls the function finishAnimation(at:) to mark the animation as completed, set the state as inactive and eventually call any completion block (more on that later).

Animation options

As you have probably noticed with the previous example, together with the animation block we have defined two parameters: the duration of the animation and the animation curve, a UIViewAnimationCurve instance that can represents the most common curves (easeIn, easeOut, linear or easeInOut).

In case you needed more control over the animation curve you can use a custom bezièr curve defined by 2 control points.

let animator = UIViewPropertyAnimator(
               duration: 1.0, 
               point1: CGPoint(0.1,0.5), 
               point2: CGPoint(0.5, 0.2){

        AView.alpha = 0.0
}

(If the bezier curves are not enough you could even specify a completely custom curve with a UITimigCurveProvider)

Another interesting option that you can pass to the constructor is the dampingRatio value. Similarly to the UIView animation helpers, you can define a spring effect specifying a damping value from 0 to 1.

let animator = UIViewPropertyAnimator(
               duration: 1.0,
               dampingRatio:0.4){

        AView.center = CGPoint(x:0, y:0)
}

Delaying the animation is quite easy too, just call the startAnimation function with the afterDelay param.

animator.startAnimation(afterDelay:2.5)

Animation Blocks

UIViewPropertyAnimator adopts the UIViewImplicitlyAnimating protocol that provides the animator with some other interesting abilities. As example, you can specify multiple animations blocks in addition to the first one specified during initialization.

// Initialization
let animator = UIViewPropertyAnimator(duration: 2.0, curve: .easeOut){
	AView.alpha = 0.0
}
// Another animation block
animator.addAnimation{ 
	Aview.center = aNewPosition
}
animator.startAnimation()

You can also add animations block to an animation the is already running, the block will be immediately executed using the remaining time as duration of the new animation.

Interacting with the animation flow

As we have already stated we can easily interact with the animation flow calling startAnimation, stopAnimation and pauseAnimation. The default flow of the animation, from the start to the end point, can be modified through the fractionComplete property. This value indicates the percentage of completion of the animation with a value that goes from 0.0 to 1.0. You can modify the value to drive the flow as you prefer (example: the user might change the fraction in real time using a slider or a pan gesture).

animator.fractionComplete = slider.value

In some cases you might want to perform actions when the animation completes its running. The addCompletion function let you add a block that will be triggered when the animation completes.

animator.addCompletion { (position) in
	print("Animation completed")
}

The position is a UIViewAnimatingPosition and it specifies whether the animation stopped a its starting, end or current position. Normally you will receive the end value.

That’s all for this quick guide.
I can’t wait to play more with this new animation system to implement some really nice UI effects! I’ll share my experiments on Twitter ;) Ciao!