前语

从一开始,动画便是 SwiftUI 最强壮的功能之一。你能够在 SwiftUI 中快速构建流通的动画。仅有的缺点是每当咱们需求运行多步动画或将动画规模限制到视图层次结构的特定部分时,咱们如何操控动画。

简单示例

让咱们从一个简单的示例开始,展现咱们旧办法的一些缺点,这些办法用于在 SwiftUI 中驱动动画。

struct ContentView: View {
    @State private var isHidden = false
    var body: some View {
        VStack {
            Button("Animate") {
                isHidden.toggle()
            }
            HugeView()
                .opacity(isHidden ? 0.0 : 1.0)
            AnotherHugeView()
        }
        .animation(.default)
    }
}

如上例所示,咱们有一个包含按钮和两个视图的视图层次结构,这些视图放置在笔直仓库中。咱们将动画视图修饰符附加到整个仓库,以动画仓库内的任何更改。

当咱们按下按钮时,仓库会动画显现内部的任何更改。可是,动画视图修饰符不连接到 isHidden 特点,这意味着它将动画显现可能发生的任何更改。其中一些更改可能是意外的,比如环境值的改变。

动画视图修饰符

咱们能够经过运用动画视图修饰符的另一个版原本消除意外动画,在这个版别中,咱们能够绑定到特定值,并且仅在值更改时进行动画处理。

struct ContentView: View {
    @State private var isHidden = false
    var body: some View {
        VStack {
            Button("Animate") {
                isHidden.toggle()
            }
            HugeView()
                .opacity(isHidden ? 0.0 : 1.0)
            AnotherHugeView()
        }
        .animation(.default, value: isHidden)
    }
}

在上面的示例中,咱们运用了带有 value 参数的动画视图修饰符。它允许咱们将动画规模限制为单个值,并仅在与特定值相关的更改时执行动画。在这种情况下,咱们没有任何意外的动画。

运用多个可动画特点

假如咱们有多个可动画特点怎么办?

在这种情况下,咱们有必要为每个可动画特点附加一个动画修饰符。这个解决方案非常有效,但在人体工程学方面有一个缺点。

struct ContentView: View {
    @State private var firstStep = false
    @State private var secondStep = false
    var body: some View {
        VStack {
            Button("Animate") {
                Task {
                    firstStep.toggle()
                    try? await Task.sleep(nanoseconds: 3_000_000_000)
                    secondStep.toggle()
                }
            }
            // 其他视图在这里
            SomeView()
                .opacity(firstStep ? 1.0 : 0.0)
                .blur(radius: secondStep ? 0 : 20.0)
        }
        .animation(.default, value: firstStep)
        .animation(.default, value: secondStep)
    }
}

走运的是,SwiftUI 引进了动画视图修饰符的一个新变体,允许咱们运用 ViewBuilder 闭包来限制动画的规模。

struct ContentView: View {
    @State private var firstStep = false
    @State private var secondStep = false
    var body: some View {
        VStack {
            Button("Animate") {
                Task {
                    firstStep.toggle()
                    try? await Task.sleep(nanoseconds: 1_000_000_000)
                    secondStep.toggle()
                }
            }
            // 其他视图在这里
            SomeView()
                .animation(.default) { content in
                    content
                        .opacity(firstStep ? 1.0 : 0.0)
                        .blur(radius: secondStep ? 0 : 20.0)
                }
        }
    }
}

如上例所示,咱们运用动画视图修饰符,供给咱们需求的动画类型和一个 ViewBuilder 闭包,在这个动画中应用。动画仅在供给的 ViewBuilder 闭包的上下文中作业,不会扩展到其他任何地方。

运用 ViewBuilder

作为起点,ViewBuilder 闭包供给一个参数,用于占位视图,在其中应用了动画视图修饰符。在 ViewBuilder 闭包内部,能够安全地对视图应用任何视图修饰符,并期望仅对此代码块进行动画处理。

struct ContentView: View {
    @State private var firstStep = false
    @State private var secondStep = false
    var body: some View {
        VStack {
            Button("Animate") {
                Task {
                    firstStep.toggle()
                    try? await Task.sleep(nanoseconds: 1_000_000_000)
                    secondStep.toggle()
                }
            }
            // 其他视图在这里
            SomeView()
                .transaction { t in
                    t.animation = t.animation?.speed(2)
                } body: { content in
                    content
                        .opacity(firstStep ? 1.0 : 0.0)
                        .blur(radius: secondStep ? 0 : 20.0)
                }
        }
    }
}

正如你所看到的,SwiftUI 供给了一品种似的办法,以在视图层次结构中保护有作用域的业务。

总结

这篇文章介绍了在SwiftUI中构建动画的新办法,重点解决了在多步动画或特定视图层次结构中操控动画的挑战。经过引进带有value参数的动画修饰符,以及运用ViewBuilder闭包限制动画规模,作者展现了更准确和灵活的动画操控方法。

这种办法在处理多个可动画特点时尤其强壮。文章还提到了SwiftUI引进的一项新变体,运用ViewBuilder闭包可在动画中应用视图修饰符,有效地将动画规模限制在特定的上下文中。

最后,介绍了在 SwiftUI 中构建有作用域的业务的新办法,以保护更具准确性和可控性的动画。这些新功能在最新的平台上可用,为SwiftUI开发者供给了更强壮的动画东西。