Suppose I have a long running task whose progress is displayed in custom progress bar similar to system progress bar. A task can finish faster than animation of CALayer.bounds
from leading to trailing edge. In this case I need to fast forward animation to the trailing edge.
Currently I replace running animation with a copy but with a much shorter duration. This approach works fine but requires boilerplate code.
I'm wondering is it possible to achieve the same result but with CALayer.speed
only (eg. set it to 2 or 3)? Naive approach to pause and resume animation with higher speed yields to unexpected result: progress layer just disappears.
So, it appears you are running a "progress bar" animation by animating the bounds of a layer.
Your comments state "3 minutes" so you probably have code like this:
// create bounds animation for width = 0 to width = bounds.width
let anim = CABasicAnimation(keyPath: "bounds")
anim.fromValue = CGRect(x: 0.0, y: 0.0, width: 0.0, height: bounds.height)
anim.toValue = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
anim.fillMode = .forwards
anim.isRemovedOnCompletion = false
// 3-minute duration == 180 seconds
anim.duration = 180
// start the animation
progressLayer.add(anim, forKey: "progress")
Then, if the "task" is completed in, say, 2-minutes and 15-seconds, you want to "speed up" the remaining portion of the animation.
The simplest way - with no need for changing the layer speed - would be:
progressLayer.removeAllAnimations()
progressLayer.bounds = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
This will stop the slow animation and immediately extend the layer bounds to full width. Note that it will use CALayer
built-in animation, so it will take 0.3-seconds for the layer to reach full width.
If you want the progress bar to "snap" directly to full width, you can wrap that in a CATransaction
block and disable the default 0.3-second animation:
CATransaction.begin()
CATransaction.setDisableActions(true)
progressLayer.removeAllAnimations()
progressLayer.bounds = CGRect(x: 0.0, y: 0.0, width: bounds.width, height: bounds.height)
CATransaction.commit()
If you actually want the animation to continue, but the default 0.3-seconds is too quick, you can change the layer speed like this:
progressLayer.timeOffset = progressLayer.convertTime(CACurrentMediaTime(), from: nil)
progressLayer.beginTime = CACurrentMediaTime()
// changing the layer speed to 2.0 would only *very slightly* increase the speed
// start with the same value as the duration and observe the result
// adjust this value until you're happy with the "speeded up" animation completion
progressLayer.speed = 180.0