在 SwiftUI 中的作用域动画

前言

从一开始,动画就是 SwiftUI 最强大的功能之一。你可以在 SwiftUI 中快速构建流畅的动画。唯一的缺点是每当我们需要运行多步动画或将动画范围限定到视图层次结构的特定部分时,我们如何控制动画。

简单示例

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

swift 复制代码
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 属性,这意味着它将动画显示可能发生的任何更改。其中一些更改可能是意外的,比如环境值的变化。

动画视图修饰符

我们可以通过使用动画视图修饰符的另一个版本来消除意外动画,在这个版本中,我们可以绑定到特定值,并且仅在值更改时进行动画处理。

swift 复制代码
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 参数的动画视图修饰符。它允许我们将动画范围限定为单个值,并仅在与特定值相关的更改时执行动画。在这种情况下,我们没有任何意外的动画。

使用多个可动画属性

如果我们有多个可动画属性怎么办?

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

swift 复制代码
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 闭包来限定动画的范围。

swift 复制代码
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 闭包内部,可以安全地对视图应用任何视图修饰符,并期望仅对此代码块进行动画处理。

swift 复制代码
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开发者提供了更强大的动画工具。

相关推荐
幸福回头2 小时前
ms-swift 代码推理数据集
llm·swift
若水无华12 小时前
fiddler 配置ios手机代理调试
ios·智能手机·fiddler
不二狗12 小时前
每日算法 -【Swift 算法】Two Sum 问题:从暴力解法到最优解法的演进
开发语言·算法·swift
Aress"13 小时前
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
ios·uni-app·ipa安装
Jouzzy1 天前
【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
安全·ios·iphone
瓜子三百克1 天前
采用sherpa-onnx 实现 ios语音唤起的调研
macos·ios·cocoa
左钦杨1 天前
IOS CSS3 right transformX 动画卡顿 回弹
前端·ios·css3
努力成为包租婆1 天前
SDK does not contain ‘libarclite‘ at the path
ios
安和昂2 天前
【iOS】Tagged Pointer
macos·ios·cocoa
I烟雨云渊T2 天前
iOS 阅后即焚功能的实现
macos·ios·cocoa