繁体   English   中英

SwiftUI In.onAppear 切换动画时出现意外动画(使用 GeometryReader 的大小)

[英]SwiftUI unexpected animations when toggling animations In .onAppear (using a GeometryReader's size)

我在 SwiftUI 中有一个奇怪的SwiftUI行为。 我试图创建一个在下面演示它的最小视图。

我想用淡入淡出和缩放效果在三个圆圈中制作动画(请参阅下面的“我期望什么”栏)。 但是,圆圈的大小取决于视图的宽度,所以我使用GeometryReader来获得它。

我想在.onAppear(perform:)中启动 animation ,但在调用时, GeometryReader尚未设置size属性。 我最终得到的是您在"Unwanted Animation 1"中看到的 animation。 这是由于帧从.zero动画到正确的大小。

但是,每当我尝试通过添加.animation(nil, value: size)修饰符来禁用帧的动画时,我都会得到一个非常奇怪的 animation 行为(请参阅“不需要的 Animation 2” )。 这个我完全不明白。 它以某种方式为 animation 添加了水平平移,使其看起来更糟。 任何想法这里发生了什么以及如何解决这个问题?

奇怪的是,如果我像这样使用明确的 animation ,一切正常:

.onAppear {
    withAnimation {
      show.toggle()
    }
}

但我想了解这里发生了什么。

谢谢!

我的期望 不需要的 Animation 1 不需要的 Animation 2
在此处输入图像描述 在此处输入图像描述 在此处输入图像描述
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()
    }
}

实际大小在第一次布局已知,但.onAppear在之前调用,因此布局(包括可动画的帧更改)位于 animation 之下。

为了解决这个问题,我们需要延迟 state 改变一点(直到第一个布局/渲染完成),比如

.onAppear {
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
        show.toggle()
    }
}

...这就是为什么withAnimation也有效的原因——它实际上将关闭调用延迟到下一个周期。

用 Xcode 13 / iOS 15 测试

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM