SwiftUI 如何用事务覆盖动画?

SwiftUI 提供了 withTransaction() 函数,该函数允许我们在运行时覆盖动画,例如,删除隐式动画并将其替换为自定义内容。

例如,此代码在小号和大号之间切换一些文本,并且由于附加了隐式动画,因此始终进行动画处理:

struct ContentView: View {
    @State private var isZoomed = false

    var body: some View {
        VStack {
            Button("Toggle Zoom") {
                isZoomed.toggle()
            }

            Spacer()
                .frame(height: 100)

            Text("Zoom Text")
                .font(.title)
                .scaleEffect(isZoomed ? 3 : 1)
                .animation(.easeInOut(duration: 2))
        }
    }
}

Transactions 使我们可以逐案地覆盖现有动画。 例如,您可能决定在特定情况下希望文本的动画以快速,线性的方式发生,而不是现有的动画。

为此,请首先使用所需的动画创建一个新的 Transaction 实例,然后将其 disablesAnimations 值设置为 true,以便覆盖将应用的所有现有动画。 准备就绪后,请使用交易对象调用 withTransaction(),然后继续调整您要更改的所有状态-所有交易都会通过交易进行动画处理。

为了演示这一点,这里是我们相同的文本缩放示例代码,除了使用事务来插入自定义动画以覆盖隐式动画之外,它还包括:

struct ContentView: View {
    @State private var isZoomed = false

    var body: some View {
        VStack {
            Button("Toggle Zoom") {
                var transaction = Transaction(animation: .linear)
                transaction.disablesAnimations = true

                withTransaction(transaction) {
                    isZoomed.toggle()
                }
            }

            Spacer()
                .frame(height: 100)

            Text("Zoom Text")
                .font(.title)
                .scaleEffect(isZoomed ? 3 : 1)
                .animation(.easeInOut(duration: 2))
        }
    }
}

为了获得更多控制,您可以将 transaction() 修饰符附加到所需的任何视图,从而允许您覆盖应用于该视图的所有事务。

例如,我们可以在示例中添加第二个缩放文本视图,仍然使用事务来触发缩放动画,但是这次我们将在第二个文本视图上使用 transaction() 修饰符,因此我们将禁用任何事务 一个视图–实际上,我们正在覆盖替代项:

struct ContentView: View {
    @State private var isZoomed = false

    var body: some View {
        VStack {
            Button("Toggle Zoom") {
                var transaction = Transaction(animation: .linear)
                transaction.disablesAnimations = true

                withTransaction(transaction) {
                    isZoomed.toggle()
                }
            }

            Spacer()
                .frame(height: 100)

            Text("Zoom Text 1")
                .font(.title)
                .scaleEffect(isZoomed ? 3 : 1)

            Spacer()
                .frame(height: 100)

            Text("Zoom Text 2")
                .font(.title)
                .scaleEffect(isZoomed ? 3 : 1)
                .transaction { t in
                    t.animation = .none
                }
        }
    }
}