NEW: Nominations are now open for the 2019 Swift Community Awards! >>

How to animate views using UIViewPropertyAnimator

Swift version: 5.1

Paul Hudson    @twostraws   

iOS 10 introduced a new closure-based animation class in the form of UIViewPropertyAnimator. Amongst other things, it lets you interactively adjust the position of an animation, making it jump to any point in time that we need – a technique commonly called scrubbing.

To try it yourself, create a new Single View App project targeting iPad, then lock it so that it supports landscape only and use Interface Builder to embed its view controller inside a navigation controller.

To demonstrate animation scrubbing we’re going to create a UISlider then fix it to the bottom of our view, spanning the full width.

Open ViewController.swift and add this code to viewDidLoad():

let slider = UISlider()
slider.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(slider)

slider.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
slider.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true

When that slider is dragged from left to right, it will count from 0 to 1 and we’re going to use that to manipulate an animation of a red box sliding across the screen.

Add this code to viewDidLoad():

let redBox = UIView(frame: CGRect(x: -64, y: 0, width: 128, height: 128))
redBox.translatesAutoresizingMaskIntoConstraints = false
redBox.backgroundColor = UIColor.red
redBox.center.y = view.center.y
view.addSubview(redBox)

That creates a 128x128 red box, centered vertically and part-way off the left edge of the screen. Even though we’re going to manipulate it elsewhere in the app, we don’t need a property for it – UIViewPropertyAnimator works using closures, so it will capture the box for us.

Next, add a property for the animator:

var animator: UIViewPropertyAnimator!

We’re going to make the animation move the box from the left to the right, while spinning around and scaling down to nothing. All that will happen over two seconds, with an ease-in-ease-out curve. Add this to the end of viewDidLoad():

animator = UIViewPropertyAnimator(duration: 2, curve: .easeInOut) { [unowned self, redBox] in
    redBox.center.x = self.view.frame.width
    redBox.transform = CGAffineTransform(rotationAngle: CGFloat.pi).scaledBy(x: 0.001, y: 0.001)
}

That doesn’t actually run the animation, which is OK for now. Instead, it creates the animation and stores it away in the animator property, ready for us to manipulate.

At this point, we have a slider on the screen and a red box too, so we just need to connect it all. When the slider is moved, its .valueChanged event will be triggered, and we can add a method to catch that. We can actually feed the slider’s value property – the number from 0.0 to 1.0 – directly into the fractionComplete property of our UIViewPropertyAnimator, which controls how much of the animation has happened, and UIKit will take care of the rest for us.

Add this method to ViewController:

@objc func sliderChanged(_ sender: UISlider) {
    animator.fractionComplete = CGFloat(sender.value)
}

To make that get called by the slider, add this to viewDidLoad():

slider.addTarget(self, action: #selector(sliderChanged), for: .valueChanged)

That’s it! We’ve created the user interface, prepared an animation, then connected the slider’s value to the animation’s progress. If you run the app now you’ll see you can drag the slider from left to right and back again to manipulate the box – you literally have exact control over its position in the animation.

If you wanted to make the animation play the traditional way – i.e., without user control – just call its startAnimation() method. You can also set animator.isReversed = true to force the animation to move backwards, ultimately returning to its starting state.

SAVE 20% ON iOS CONF SG The largest iOS conference in Southeast Asia is back in Singapore for the 5th time in January 2020, now with two days of workshops plus two days of talks on SwiftUI, Combine, GraphQL, and more! Save a massive 20% on your tickets by clicking on this link.

Available from iOS 10.0

Similar solutions…

About the Swift Knowledge Base

This is part of the Swift Knowledge Base, a free, searchable collection of solutions for common iOS questions.

MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 3.7/5