💡 如何屏蔽 Sheet、NavigationStack 的转场动画

TL;DR: 通过自定义 Transaction 并设置 disablesAnimations = true,可以屏蔽 SwiftUI 中 SheetNavigationStack 的转场动画,实现无动画的状态切换效果。

背景

SwiftUI 默认为许多组件(如 SheetFullScreenCoverNavigationStack 等)提供转场动画。在某些特殊场景(例如 Deep Link 跳转)中,开发者可能希望直接进入目标状态,而不需要转场动画。

实现方法

尽管无法直接关闭这些组件的转场动画,但可以通过自定义 Transaction,将 disablesAnimations 设置为 true,实现屏蔽转场动画的效果。

示例:屏蔽 Sheet 动画

Swift
struct SheetDemo: View {
    @State private var isActive = false
    var body: some View {
        List {
            Button("Pop Sheet without Animation") {
                var transaction = Transaction(animation: .none)
                transaction.disablesAnimations = true
                withTransaction(transaction) {
                    isActive.toggle()
                }
            }
            Button("Pop Sheet with Animation") {
                isActive.toggle()
            }
        }
        .sheet(isPresented: $isActive) {
            VStack {
                Button("Dismiss without Animation") {
                    var transaction = Transaction(animation: .none)
                    // disable animation
                    transaction.disablesAnimations = true
                    withTransaction(transaction) {
                        isActive.toggle()
                    }
                }
                Button("Dismiss with Animation") {
                    isActive.toggle()
                }
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

效果演示

禁用 Sheet 动画

示例:屏蔽 NavigationStack 动画

Swift
struct NavigationStackDemo: View {
    @State var pathStore = PathStore()
    var body: some View {
        @Bindable var pathStore = pathStore
        NavigationStack(path: $pathStore.path) {
            List {
                Button("Go Link without Animation") {
                    var transaction = Transaction(animation: .none)
                    // disable animation
                    transaction.disablesAnimations = true
                    withTransaction(transaction) {
                        pathStore.path.append(1)
                    }
                }
                Button("Go Link with Animation") {
                    pathStore.path.append(1)
                }
            }
            .navigationDestination(for: Int.self) {
                ChildView(store: pathStore, n: $0)
            }
        }
    }
}

@Observable
class PathStore {
    var path: [Int] = []
}

struct ChildView: View {
    let store: PathStore
    let n: Int
    @Environment(\.dismiss) var dismiss
    var body: some View {
        List {
            Text("\(n)")
            Button("Dismiss without Animation") {
                var transaction = Transaction(animation: .none)
                transaction.disablesAnimations = true
                withTransaction(transaction) {
                    store.path = []
                }
            }
            Button("Dismiss with Animation") {
                dismiss()
            }
        }
    }
}

效果演示

禁用 NavigationStack 动画

注意事项

  1. 局部影响 Transaction 的设置仅影响包含在 withTransaction 闭包中的操作,不会改变其他操作的动画行为。

  2. 动画屏蔽规则

    由于 withTransaction 会屏蔽闭包中所有状态变化引发的动画,因此应避免将无关的状态变化包含在用于屏蔽转场动画的显式命令中,以免意外禁用其他动画效果。

延伸阅读

为您每周带来有关 Swift 和 SwiftUI 的精选资讯!