I want to achieve an animate: Each cell rightImage move first, and after rightImage moved, the backgroundImage(which is a gif) start to animating. However, the moving animation and gif startAnimating always start at the same time. Like the demo below:

Here is Debug Log which print moving timestamp and animating timestamp:
<<< start index: 0
>>> start transfrom 2023-04-04 10:32:39
>>> start animating 2023-04-04 10:32:39
<<< start index: 1
>>> start transfrom 2023-04-04 10:32:43
>>> start animating 2023-04-04 10:32:43
<<< start index: 2
>>> start transfrom 2023-04-04 10:32:47
>>> start animating 2023-04-04 10:32:47
Here is my animation code:
func startMoving() {
let duration: Double = 4
UIView.animateKeyframes(withDuration: duration, delay: 0, options: [.calculationModeLinear], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.7/duration, animations: {
print(">>> start transfrom")
self.frontImageView.transform = CGAffineTransform(translationX: -20, y: 0)
})
UIView.addKeyframe(withRelativeStartTime: 0.7/duration, relativeDuration: 1.5/duration, animations: {
print(">>> start animating")
self.backgroundImageView.startAnimating()
})
}, completion: { _ in
self.backgroundImageView.stopAnimating()
})
}
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:
and you want the second keyframe to start when the first animation ends (so, 70% from the start), and run for the remainder:
So, let's changing the timing parameters, and add a couple more
print()statements:What you'll see in the debug console is immediately:
and at the end of 4-seconds:
However, you'll still see the animation begin at Zero.
Keyframe animation does not wait to execute code. Inside the
UIView.animateKeyframesblock, UIKit analyzes all of theaddKeyFramesegments 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: