Search code examples
swiftuiresetnavigationview

SwiftUI navigate to bottom of NavigationView stack


I have the following set up where a parent view holds a NavigationView which displays a series of pages, A, B and C. On page C there is a button which hides the navigation view. I want to have it so that when the navigation view is shown again, it automatically navigates to page A, however I am unsure how to do this with SwiftUI, how can this be achieved?

struct ParentView: View {
    @State var showNavigation:Bool = true
    var body: some View {
        ZStack {
            Button(action: {
                self.showNavigation = true
            }) {
                Text("Show navigation")
            }
            NavigationView {
                NavigationLink(destination: ChildA(showNavigation: $showNavigation)) {
                    Text("Go to A")
                }
            }.opacity(showNavigation ? 1.0 : 0.0)
        }
    }
}

struct ChildA: View {
    @Binding var showNavigation:Bool

    var body: some View {
        VStack {
            Text("A")
            NavigationLink(destination: ChildB(showNavigation: $showNavigation)) {
                           Text("Go to B")
                       }
        }
    }
}

struct ChildB: View {
    @Binding var showNavigation:Bool

    var body: some View {
        VStack {
            Text("B")
            NavigationLink(destination: ChildC(showNavigation: $showNavigation)) {
                           Text("Go to C")
                       }
        }
    }
}

struct ChildC: View {
    @Binding var showNavigation:Bool

    var body: some View {
        VStack {
            Text("C")
            Button(action: {
                self.showNavigation = false
            }) {
                Text("Hide Navigation")
            }
        }
    }
}

Flow


Solution

  • The setting here is not complicated. One thing is for any intermediate view, you have to set .isDetailLink(false). Otherwise, they will be kept during rewinding.

    struct ParentView: View {
        @State var showNavigation:Bool = true
        @State var isActive:Bool = true
    
        var body: some View {
            ZStack {
                Button(action: {
                    self.showNavigation = true
                }) {
                Text("Show navigation")
            }
            NavigationView {
                NavigationLink.init(
                    destination: ChildA(showNavigation: $showNavigation, isActive: $isActive), 
                    isActive: $isActive) {
                        Text("Go to A")
                    }
                }.opacity(showNavigation ? 1.0 : 0.0)
            }
        }
    }
    
    struct ChildA: View {
        @Binding var showNavigation:Bool
        @Binding var isActive:Bool
        @State var isNextActive:Bool = false
        var body: some View {
            VStack {
                Text("A")
                NavigationLink(
                    destination: ChildB(showNavigation: $showNavigation, isActive: $isNextActive), 
                    isActive: $isNextActive) {
                         Text("Go to B")
                    }.isDetailLink(false)
                }.onReceive(Just(isNextActive)) { isNextActive in
                    if isNextActive == false && (!self.showNavigation) {
    
                        self.isActive = false
                    }
                }
            }
        }
    }
    
    struct ChildB: View {
        @Binding var showNavigation:Bool
        @Binding var isActive:Bool
        @State var isNextActive:Bool = false
    
        var body: some View {
            VStack {
                Text("B")
                NavigationLink(destination: ChildC(showNavigation: $showNavigation, isActive: $isNextActive), isActive: $isNextActive) {
                    Text("Go to C")
                }.isDetailLink(false)
            }.onReceive(Just(isNextActive)) { isNextActive in
                if isNextActive == false && (!self.showNavigation) {
                    DispatchQueue.main.async {
                        self.isActive = false
                    }
                }
            }             
        }
    }
    
    struct ChildC: View {
        @Binding var showNavigation:Bool
        @Binding var isActive:Bool
        var body: some View {
            VStack {
                Text("C")
                Button(action: {
                    self.showNavigation = false
                    self.isActive = false
                }) {
                    Text("Hide Navigation")
                }
            }
        }
    }