Qua*_*ntm 4 animation ios swift swiftui
我有一个奇怪的动画行为SwiftUI。我尝试创建一个最小视图来演示它,如下所示。
我想用淡入淡出和缩放效果制作三个圆圈的动画(请参阅下面的“我的期望”栏)。但是,圆圈的大小取决于视图的宽度,因此我使用 aGeometryReader来实现。
我想在 中启动动画.onAppear(perform:),但在调用时,GeometryReader尚未设置size属性。我最终得到的是你在“Unwanted Animation 1”中看到的动画。这是因为帧被动画化为.zero正确的尺寸。
但是,每当我尝试通过添加修改器来禁用帧的动画时.animation(nil, value: size),我都会得到极其奇怪的动画行为(请参阅“不需要的动画 2”)。这一点我完全不明白。它以某种方式为动画添加了水平平移,这使得它看起来更糟糕。您知道这里发生了什么以及如何解决这个问题吗?
奇怪的是,如果我使用如下所示的显式动画,一切都会正常工作:
.onAppear {
withAnimation {
show.toggle()
}
}
Run Code Online (Sandbox Code Playgroud)
但我想了解这里发生了什么。
谢谢!
替换.onAppear(perform:)为以下代码是否合理?这只会在视图的生命周期中触发一次,即当size更改.zero为正确值时。
.onChange(of: size) { [size] newValue in
guard size == .zero else { return }
show.toggle()
}
Run Code Online (Sandbox Code Playgroud)
import SwiftUI
struct TestView: View {
@State private var show = false
@State private var size: CGSize = .zero
var body: some View {
VStack {
circle
circle
circle
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.contentShape(Rectangle())
.background {
GeometryReader { proxy in
Color.clear.onAppear { size = proxy.size }
}
}
.onAppear { show.toggle() }
}
private var circle: some View {
Circle()
.frame(width: circleSize, height: circleSize)
.animation(nil, value: size) // This make the circles animate in from the side for some reason (see "Strange Animation 2")
.opacity(show ? 1 : 0)
.scaleEffect(show ? 1 : 2)
.animation(.easeInOut(duration: 1), value: show)
}
private var circleSize: Double {
size.width * 0.2 // Everything works fine if this is a constant
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
Run Code Online (Sandbox Code Playgroud)
实际大小在第一次布局之后已知,但在之前.onAppear调用,因此布局(包括可动画化的帧更改)处于动画状态下。
为了解决这个问题,我们需要稍微延迟状态更改(直到第一次布局/渲染完成),例如
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
show.toggle()
}
}
Run Code Online (Sandbox Code Playgroud)
...这就是为什么withAnimation也有效 - 它实际上将关闭调用延迟到下一个周期。
使用 Xcode 13 / iOS 15 进行测试