Reset NagivationView stack in TabView

Viewed 1072

I have a tabview with two tabs (tabs A and B).

Clicking tab A opens a master View. In that master view there is a navigation link to Page 1. Within Page 1 there is also a link to Page 2. When the user is on Page 1 or 2, and I tap Tab A, it doesn’t revert to master View. Similarly if the user clicks Tab B and then Tab A again, it returns to Page 1 or 2 (whichever the user was on), rather than master View.

How to I make the navigation stack reset in both cases?

Thanks!

2 Answers

That's because the View won't be rerendered. Here is a possible approach how to achieve your behavior:

You can use ProxyBinding for the TabView to detect changes and then reset the NavigationLink by changing the internal State variable.

struct ContentView: View {
    @State var activeView: Int = 0
    @State var showNavigation: Bool = false
    var body: some View {
        TabView(selection: Binding<Int>(
                    get: {
                        activeView
                    }, set: {
                        activeView = $0
                        showNavigation = false //<< when pressing Tab Bar Reset Navigation View
                    }))
        {
            NavigationView {
                NavigationLink("Click", destination: Text("Page A"), isActive: $showNavigation)
            }
            .tabItem {
                Image(systemName: "1.circle")
                Text("First")
            }
            .tag(0)
            
            Text("Second View")
            .padding()
            .tabItem {
                Image(systemName: "2.circle")
                Text("Second")
            }
            .tag(1)
        }
    }
}

You can create RootView with MainView


    import SwiftUI

    struct RootView: View {
        
        @ObservedObject var viewModel = RootViewModel()
        
        init(){
            viewModel.prepare()
        }
        var body: some View {
            MainView(tab: viewModel.mainTab)
                .id(UUID().uuidString)
        }
    }

Create RootViewModel with listeners to screen updating


    import SwiftUI

    class RootViewModel: ObservableObject{
        
        @Published var mainTab: SelectedTab = .firstTab
        let mainScreenNotification = NSNotification.Name("mainScreenNotification")
        private var observerMain: Any?
        
        
        func prepare(){
            observerMain = NotificationCenter.default.addObserver(forName: mainScreenNotification, object: nil, queue: nil, using: { [unowned self] notification in
                self.mainTab = (notification.userInfo?["selectedTab"])! as! SelectedTab
            })
        }
    }

    enum SelectedTab {
      case firstTab, secondTab
    }
        

Run this to inflating new tab screen from tab child:


    NotificationCenter.default.post(name: 
       NSNotification.Name("mainScreenNotification"), 
       object: nil,  
       userInfo: ["selectedTab": SelectedTab.firstTab]
    )
Related