Couple of things...
1 - you are using relative start time and relative duration incorrectly
2 - keyframe animation don't work the way you think they do
The relative times are a percentage of the duration.
If you want the first keyframe to start immediately, and run for 70% of the total duration, it should look like this:
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.7, animations: {
and you want the second keyframe to start when the first animation ends (so, 70% from the start), and run for the remainder:
UIView.addKeyframe(withRelativeStartTime: 0.7, relativeDuration: 0.3, animations: {
So, let's changing the timing parameters, and add a couple more print()
statements:
func startMoving() {
let duration: Double = 4
UIView.animateKeyframes(withDuration: duration, delay: 0, options: [.calculationModeLinear], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.7, animations: {
print(">>> start transfrom")
self.frontImageView.transform = CGAffineTransform(translationX: -20, y: 0)
})
UIView.addKeyframe(withRelativeStartTime: 0.7, relativeDuration: 0.3, animations: {
print(">>> start animating")
self.backgroundImageView.startAnimating()
})
}, completion: { _ in
print(">>> completion")
self.backgroundImageView.stopAnimating()
})
print(">>> end of startMoving()")
}
What you'll see in the debug console is immediately:
>>> start transfrom
>>> start animating
>>> end of startMoving()
and at the end of 4-seconds:
>>> completion
However, you'll still see the animation begin at Zero.
Keyframe animation does not wait to execute code. Inside the UIView.animateKeyframes
block, UIKit analyzes all of the addKeyFrame
segments immediately, and then "plays the animation."
That's why the resulting animation will look different depending on the KeyFrameAnimationOptions
(.calulationMode...
values).
Logically, we think that this line: self.backgroundImageView.startAnimating()
will execute at 70% of the total duration. In reality, it executes when UIKit builds the animation, which is why your image view animation begins at the start.
If you want the "front" view to slide over, and then start the image view animation, you need to play the slide-over animation and start the image view animation when that has completed.
Something along these lines:
func startMoving() {
let duration: Double = 4.0 * 0.7
UIView.animate(withDuration: duration, animations: {
print(">>> start transfrom")
self.frontImageView.transform = CGAffineTransform(translationX: -20, y: 0)
}, completion: { _ in
print(">>> start animating")
// maybe set imageView animation properties...
self.backgroundImageView.animationRepeatCount = 1
self.backgroundImageView.animationDuration = 1.0 // 1-second to cycle through all images
self.backgroundImageView.startAnimating()
})
print(">>> end of startMoving()")
}