🔍 Disable Transition Animations for Sheet and NavigationStack in SwiftUI

Get weekly handpicked updates on Swift and SwiftUI!

TL;DR: You can suppress default transition animations in SwiftUI’s Sheet and NavigationStack by using a Transaction with disablesAnimations set to true. Wrap your state changes in withTransaction to disable animations selectively.

Background

SwiftUI provides default transition animations for components like Sheet, FullScreenCover, and NavigationStack. However, in certain scenarios—such as deep link navigation—developers may prefer to directly reach the target state without transition animations.

Implementation

While it’s not possible to directly turn off transition animations for these components, you can achieve this effect by customizing a Transaction and setting disablesAnimations to true.

Example: Disabling Sheet Animations

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)
                    transaction.disablesAnimations = true
                    withTransaction(transaction) {
                        isActive.toggle()
                    }
                }
                Button("Dismiss with Animation") {
                    isActive.toggle()
                }
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

Demonstration

Disabling Sheet Animation

Example: Disabling NavigationStack Animations

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)
                    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()
            }
        }
    }
}

Demonstration

Disabling NavigationStack Animation

Considerations

  1. Localized Effect The Transaction settings only affect operations within the withTransaction closure. Other operations remain unaffected.
  2. Animation Suppression Rules Since withTransaction suppresses all animations caused by state changes within its closure, ensure that unrelated state changes are excluded to avoid inadvertently disabling other animations.

Further Reading