英文:
swift animateKeyframes withRelativeStartTime not working
问题
以下是您要翻译的内容:
"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()
})
}
英文:
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()
})
}
答案1
得分: 1
1 - 你使用相对的开始时间和相对的持续时间不正确
2 - 关键帧动画不是你想象的方式工作
相对时间是总持续时间的百分比。
如果你想让第一个关键帧立即开始,并运行总持续时间的70%,应该这样写:
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.7, animations: {
你希望第二个关键帧在第一个动画结束后开始(也就是从开始的70%开始),并持续剩余时间:
UIView.addKeyframe(withRelativeStartTime: 0.7, relativeDuration: 0.3, animations: {
所以,让我们更改定时参数,再添加一些 print()
语句:
func startMoving() {
let duration: Double = 4
UIView.animateKeyframes(withDuration: duration, delay: 0, options: [.calculationModeLinear], animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.7, animations: {
print(">>> 开始变换")
self.frontImageView.transform = CGAffineTransform(translationX: -20, y: 0)
})
UIView.addKeyframe(withRelativeStartTime: 0.7, relativeDuration: 0.3, animations: {
print(">>> 开始动画")
self.backgroundImageView.startAnimating()
})
}, completion: { _ in
print(">>> 完成")
self.backgroundImageView.stopAnimating()
})
print(">>> 结束 startMoving()")
}
在调试控制台中,你会立刻看到:
>>> 开始变换
>>> 开始动画
>>> 结束 startMoving()
并在4秒后:
>>> 完成
但是,你仍然会看到动画从零开始。
关键帧动画不会等待执行代码。在 UIView.animateKeyframes
块内部,UIKit 立即分析所有 addKeyFrame
段,然后“播放动画”。
这就是为什么最终的动画在 KeyFrameAnimationOptions
(.calulationMode...
值)不同的情况下会看起来不同的原因。
从逻辑上讲,我们认为这一行代码:self.backgroundImageView.startAnimating()
将在总持续时间的70%处执行。实际上,它在UIKit构建动画时执行,这就是为什么你的图像视图动画在开始时就开始的原因。
如果你希望“前景”视图滑动过去,然后才开始图像视图动画,你需要在滑动动画完成后播放滑动动画并开始图像视图动画。
大致如下:
func startMoving() {
let duration: Double = 4.0 * 0.7
UIView.animate(withDuration: duration, animations: {
print(">>> 开始变换")
self.frontImageView.transform = CGAffineTransform(translationX: -20, y: 0)
}, completion: { _ in
print(">>> 开始动画")
// 可能设置图像视图动画属性...
self.backgroundImageView.animationRepeatCount = 1
self.backgroundImageView.animationDuration = 1.0 // 1秒循环所有图像
self.backgroundImageView.startAnimating()
})
print(">>> 结束 startMoving()")
}
英文:
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()")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论