SwiftUI:动画绑定

animation()修饰符可以应用于任何SwiftUI绑定,从而使该值在其当前值和新值之间设置动画。如果所讨论的数据听起来并不是真正可以动画化的,比如布尔值,它甚至都可以工作——你可以想象从1.0到2.0的动画化,因为我们可以做1.05、1.1、1.15等等,但是从“false”到“true”听起来好像没有中间值的空间。

最好用一些工作代码来解释这一点,下面是一个带有VStackStepperButton的视图:

struct ContentView: View {
    @State private var animationAmount: CGFloat = 1

    var body: some View {
        VStack {
            Stepper("Scale amount", value: $animationAmount.animation(), in: 1...10)

            Spacer()

            Button("Tap Me") {
                self.animationAmount += 1
            }
            .padding(40)
            .background(Color.red)
            .foregroundColor(.white)
            .clipShape(Circle())
            .scaleEffect(animationAmount)
        }
    }
}

提示:它使用了一个简化的Stepper初始化器,如果您只需要一个文本标题,它会很好地工作,就像Button也有一个简化的文本初始化器一样。

如您所见,步进器可以上下移动animationAmount,点击按钮将向其添加1,它们都绑定到相同的数据,这反过来会导致按钮的大小发生变化。但是,点击按钮会立即更改动画计数,因此按钮将跳到其更大的大小。相比之下,步进器绑定到$animationAmount.animation(),这意味着SwiftUI将自动设置其更改的动画。

现在,作为一个实验,我希望你把body的开始改为:

var body: some View {
    print(animationAmount)
    
    return VStack {

因为我们有一些非视图代码,我们需要在VStack之前添加return,这样Swift就知道哪个部分是被发送回来的视图。但是添加print(animationAmount)很重要,为了了解为什么我希望您再次运行该程序并尝试操作步进器。

你应该看到它输出了2.0,3.0,4.0等等。同时,该按钮可以平滑地上下缩放,而不仅仅是直接跳到缩放2、3和4。这里实际发生的情况是,SwiftUI在绑定更改之前检查我们视图的状态,在绑定更改之后检查我们视图的目标状态,然后应用动画从点A到点B。

这就是为什么我们可以设置布尔值更改的动画:Swift并不是以某种方式在false和true之间创建新值,而是设置视图更改的动画,这些视图变化是由值更改引起的。

这些绑定动画使用的animation()修饰符与我们在视图上使用的相同,因此如果您希望您可以这样写:

Stepper("Scale amount", value: $animationAmount.animation(
    Animation.easeInOut(duration: 1)
        .repeatCount(3, autoreverses: true)
), in: 1...10)

这些绑定动画有效地改变了隐式动画原来的写法:与其在视图上设置动画并使用状态更改来隐式设置动画,不如在视图上不设置任何内容,并使用状态更改来显式设置动画。在前者中,状态更改不知道它将触发动画,在后者中,视图不知道它将被动画——这两种方式都有效且都很重要。

Animating bindings

Previous: 自定义动画 Hacking with iOS: SwiftUI Edition Next: 创建显式动画

赏我一个赞吧~~~

你可能感兴趣的:(SwiftUI:动画绑定)