SwiftUI 动画无法使用动画(_:值:)

Dan*_*Dan 7 swiftui swiftui-animation

在 SwiftUI 中,我成功地使用修改器在视图第一次绘制到屏幕上时使按钮具有动画效果animation(_:),这在 macOS 12 中已被弃用。

我尝试用新的animation(_:value:)修饰符替换它,但是这次没有任何反应:所以这不起作用:

struct ContentView: View {
    @State var isOn = false
    var body: some View {
        Button("Press me") {
            isOn.toggle()
        }
        .animation(.easeIn, value: isOn)
        .frame(width: 300, height: 400)
    }
}
Run Code Online (Sandbox Code Playgroud)

但这是有效的。为什么?

struct ContentView: View {
    var body: some View {
        Button("Press me") {
        }
        .animation(.easeIn)
        .frame(width: 300, height: 400)
    }
}
Run Code Online (Sandbox Code Playgroud)

第二个示例在视图显示时为按钮设置动画,而第一个示例不执行任何操作

Yrb*_*Yrb 12

The difference between animation(_:) and animation(_:value:) is straightforward. The former is implicit, and the latter explicit. The implicit nature of animation(_:) meant that anytime ANYTHING changed, it would react. The other issue it had was trying to guess what you wanted to animate. As a result, this could be erratic and unexpected. There were some other issues, so Apple has simply deprecated it.

animation(_:value:) is an explicit animation. It will only trigger when the value you give it changes. This means you can't just stick it on a view and expect the view to animate when it appears. You need to change the value in an .onAppear() or use some value that naturally changes when a view appears to trigger the animation. You also need to have some modifier specifically react to the changed value.

struct ContentView: View {
    @State var isOn = false
    //The better route is to have a separate variable to control the animations
    // This prevents unpleasant side-effects.
    @State private var animate = false
    
    var body: some View {
        VStack {
            Text("I don't change.")
                .padding()
            Button("Press me, I do change") {
                isOn.toggle()
                animate = false
                // Because .opacity is animated, we need to switch it
                // back so the button shows.
                DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                    animate = true
                }
            }
            // In this case I chose to animate .opacity
            .opacity(animate ? 1 : 0)
            .animation(.easeIn, value: animate)
            .frame(width: 300, height: 400)
            // If you want the button to animate when the view appears, you need to change the value
            .onAppear { animate = true }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)