I've been looking at the very cool new UIViewPropertyAnimator
class in iOS 10. It lets you easily do things like pause, resume, and reverse in-flight UIView animations. It used to be that you had to manipulate the underlying CAAnimations the system created in order to do that with UIView animations.
The new UIViewPropertyAnimator
class has a property fractionComplete
. It varies from 0.0 (beginning of animation) to 1.0 (end of animation.) If you change the value, you can "scrub" an animation from beginning to end.
I have a demo project on Github called KeyframeViewAnimations that lets you use a slider to scrub UIView and CAAnimations back and forth using fairly arcane CAAnimation tricks. As the animation runs, the slider moves from beginning to end to show the progress of the animation. That project was written in Objective-C before Swift was released, but the underlying techniques are the same in Objective-C or Swift.
I decided to create an equivalent project using UIViewPropertyAnimator
. There are some quirks, but it's fairly straightforward. One thing I could not figure out how to do cleanly was to observe the progress of the animation's fractionComplete
property so that I could update the slider as the animation progresses. I tried setting up KVO (key-value-observing) on the fractionComplete
property, but it doesn't trigger a KVO notification until the animation is complete.
What I ended up doing is setting up a timer that runs on a very small interval and queries the UIViewPropertyAnimator
's fractionComplete
property repeatedly and updating the slider as it progresses. It works, but it's not a very clean way of doing things. It would be much better if there was a way to get notified about the progress of the animation.
I'm hoping there's something I'm missing.
You can see the UIViewPropertyAnimator
based project on Github at this link: UIViewPropertyAnimator-test.
The code that gets the progress value from the animator is this code:
func startTimer(_ start: Bool) {
if start {
timer = Timer.scheduledTimer(withTimeInterval: 0.02,
repeats: true) {
timer in
var value = Float(self.viewAnimator.fractionComplete)
if self.animatingBackwards {
value = 1 - value
}
self.animationSlider.value = value
}
}
else {
self.timer?.invalidate()
}
}
I should probably convert the code above to use a CADisplayLink
timer instead of a vanilla Timer (NSTimer) but I'm hoping there's a better way.
FYI, after researching this, the answer appears to be that no, there is no way to get updates as your animation progresses. You have to set up a timer (Either a Timer
or a CADisplayLink
timer) and update your progress value each time it fires.