How to disable NavigationView push and pop animations
Asked Answered
W

4

38

Given this simple NavigationView:

struct ContentView : View {
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink("Push Me", destination: Text("PUSHED VIEW"))
            }
        }
    }
}

Did anyone find a way of disabling the NavigationView animation when a destination view is pushed/popped into/from the stack?

This has been possible in UIKit since iOS2.0! I think it is not too much to ask from the framework. I tried all sorts of modifiers on all views (i.e., the NavigationView container, the destination view, the NavigationLink, etc)

These are some of the modifiers I tried:

.animation(nil)
.transition(.identity)
.transaction { t in t.disablesAnimations = true }
.transaction { t in t.animation = nil }

None made a difference. I did not find anything useful in the EnvironmentValues either :-(

Am I missing something very obvious, or is the functionality just not there yet?

Weatherproof answered 1/8, 2019 at 9:8 Comment(0)
K
44

Xcode 11.3:

Right now there is no modifier to disable NavigationView animations.

You can use your struct init() to disable animations, as below:

struct ContentView : View {

    init(){
        UINavigationBar.setAnimationsEnabled(false)
    }

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink("Push Me", destination: Text("PUSHED VIEW"))
            }
        }
    }
}
Kleiman answered 25/12, 2019 at 19:40 Comment(1)
Using UINavigationBar.setAnimationsEnabled(false) seems to be the only workaround for Xcode 14 and NavigationStack.Retharethink
W
21

iOS & iPadOS 16.2 Release Notes

SwiftUI, New Features

  • Disable animations of NavigationStack push and pop by wrapping path mutation in withTransaction(transaction) { … } where transaction has disablesAnimations set to true. (88993253)

Earlier iOS

First you need state for the NavigationLink to respond to, then set that state inside a transaction with animations disabled, as follows:

struct ContentView : View {
    @State var isActive = false
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(isActive: $isActive, destination: {
                    Text("PUSHED VIEW")}) {
                    Text("Push Me")
                }
                Button("Navigate Without Animation") {
                    var transaction = Transaction()
                    transaction.disablesAnimations = true
                    withTransaction(transaction) {
                        isActive = true
                    }
                }
            }
        }
    }
}
Withrow answered 9/12, 2021 at 16:19 Comment(3)
@joshuakcockrell You are misleading, I tested it on 16.1 and it worksGermann
@Germann Cool. This was a reported bug they must have fixed. I'll delete my comment.Expanse
This works for the push animation only - pop animation still happens. If you are manually setting the flag to false somewhere, you can also put that in the transaction block, but if you are relying on the system back button, you are still stuck with the animation.Atchley
H
5

I recently created an open source project called swiftui-navigation-stack (https://github.com/biobeats/swiftui-navigation-stack) that contains the NavigationStackView, a view that mimics the navigation behaviours of the standard NavigationView adding some useful features. For example, you could use the NavigationStackView and disable the transition animations as requested by Kontiki in the question. When you create the NavigationStackView just specify .none as transitionType:

struct ContentView : View {
    var body: some View {
        NavigationStackView(transitionType: .none) {
            ZStack {
                Color.yellow.edgesIgnoringSafeArea(.all)

                PushView(destination: View2()) {
                    Text("PUSH")
                }
            }
        }
    }
}

struct View2: View {
    var body: some View {
        ZStack {
            Color.green.edgesIgnoringSafeArea(.all)
            PopView {
                Text("POP")
            }
        }
    }
}

PushView and PopView are two views that allow you push and pop views (similar to the SwiftUI NavigationLink). Here is the complete example:

import SwiftUI
import NavigationStack

struct ContentView : View {
    var body: some View {
        NavigationStackView(transitionType: .none) {
            ZStack {
                Color.yellow.edgesIgnoringSafeArea(.all)

                PushView(destination: View2()) {
                    Text("PUSH")
                }
            }
        }
    }
}

struct View2: View {
    var body: some View {
        ZStack {
            Color.green.edgesIgnoringSafeArea(.all)
            PopView {
                Text("POP")
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

The result is:

enter image description here

It would be great if you guys joined me in improving this open source project.

Haines answered 4/2, 2020 at 15:41 Comment(0)
P
0

Since iOS and iPadOS 16.2, you can disable animations of NavigationStack, as indicated in iOS & iPadOS 16.2 Release Notes.

The following example shows a possible implementation to programmatically disable animations when a View is pushed or popped:

enum Destination {
    case details
}
import SwiftUI

struct ContentView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            Form {
                NavigationLink(value: Destination.details) {
                    Text("Push (with animation)")
                }
                Button("Push (no animation)") {
                    var transaction = Transaction()
                    transaction.disablesAnimations = true
                    withTransaction(transaction) {
                        path.append(Destination.details)
                    }
                }
            }
            .navigationDestination(for: Destination.self) { destination in
                switch destination {
                case .details:
                    DetailsView(path: $path)
                }
            }
        }
    }
}
import SwiftUI

struct DetailsView: View {
    @Binding var path: NavigationPath

    var body: some View {
        Button("Pop (no animation)") {
            var transaction = Transaction()
            transaction.disablesAnimations = true
            withTransaction(transaction) {
                path.removeLast()
            }
        }
    }
}
Pacorro answered 30/7, 2023 at 12:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.