我正在尝试在不同的视图上为一些不同的属性设置动画。这些动画都使用相同的
Animation
(具有相同的持续时间),并且它们都由相同的属性驱动。然而,我发现它们很快就变得不同步,我不明白为什么。这是我的代码:
import SwiftUI
struct ContentView: View {
@State var isAnimating = false
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 8, style: .continuous)
.fill(Color.yellow)
.offset(x: isAnimating ? -30 : 0)
.rotationEffect(isAnimating ? .degrees(-10) : .zero)
RoundedRectangle(cornerRadius: 8, style: .continuous)
.fill(Color.green.opacity(0.5))
.offset(x: isAnimating ? 30 : 0)
.rotationEffect(isAnimating ? .degrees(10) : .zero)
RoundedRectangle(cornerRadius: 8, style: .continuous)
.fill(Color.red)
.scaleEffect(isAnimating ? 1.1 : 1)
}
.frame(width: 100, height: 100)
.padding()
.onAppear {
let animation = Animation.bouncy(duration: 0.5)
.repeatForever(autoreverses: true)
withAnimation(animation) {
isAnimating = true
}
}
}
}
#Preview {
ContentView()
}
我发现的唯一解决方法是使用与
Animation
不同的 Animation.bouncy
,例如Animation.linear
或Animation.easeInOut
。在这种情况下,动画全部保持同步。这让我相信这与 Animation.bouncy
有关(其他弹性动画也存在同样的问题,例如 Animation.spring
)。
此问题出现在 SwiftUI 预览版、设备上和 iOS 模拟器上(全部使用截至撰写时的最新软件:iOS 17.4、Xcode 15.3)。
解决方法是与
Animation.interpolatingSpring(duration: 0.5)
由于
.bouncy
是自定义的弹簧动画,因此您可以使用interpolatingSpring并达到所需的结果
根据文档:
插值弹簧动画,使用阻尼弹簧模型生成 [0, 1] 范围内的值,然后使用这些值在动画属性的 [from, to] 范围内进行插值。通过添加每个动画的效果来保持重叠动画的速度。
这对我来说看起来像是一个错误。涉及旋转和偏移的动画不考虑动画持续时间,并且在结束时有延迟。延迟几乎与动画本身一样长(如果给动画更长的持续时间,延迟会更明显)。
作为解决方法,您可以使用
PhaseAnimator
将向前和向后动画链接在一起,而不是使用 .repeatForever(autoreverses: true)
:
struct ContentView: View {
var body: some View {
PhaseAnimator([false, true]) { flag in
ZStack {
RoundedRectangle(cornerRadius: 8, style: .continuous)
.fill(Color.yellow)
.offset(x: flag ? -30 : 0)
.rotationEffect(flag ? .degrees(-10) : .zero)
RoundedRectangle(cornerRadius: 8, style: .continuous)
.fill(Color.green.opacity(0.5))
.offset(x: flag ? 30 : 0)
.rotationEffect(flag ? .degrees(10) : .zero)
RoundedRectangle(cornerRadius: 8, style: .continuous)
.fill(Color.red)
.scaleEffect(flag ? 1.1 : 1)
}
.animation(.bouncy(duration: 0.5), value: flag)
}
.frame(width: 100, height: 100)
.padding()
}
}