TL;DR: 通过自定义
Transaction
并设置disablesAnimations = true
,可以屏蔽 SwiftUI 中Sheet
和NavigationStack
的转场动画,实现无动画的状态切换效果。
背景
SwiftUI 默认为许多组件(如 Sheet
、FullScreenCover
、NavigationStack
等)提供转场动画。在某些特殊场景(例如 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)
}
}
}
效果演示
示例:屏蔽 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()
}
}
}
}
效果演示
注意事项
-
局部影响
Transaction
的设置仅影响包含在withTransaction
闭包中的操作,不会改变其他操作的动画行为。 -
动画屏蔽规则
由于
withTransaction
会屏蔽闭包中所有状态变化引发的动画,因此应避免将无关的状态变化包含在用于屏蔽转场动画的显式命令中,以免意外禁用其他动画效果。