Search code examples
swiftuiswiftui-tabviewios16xcode14swiftui-navigationstack

How to popToRoot with the new iOS 16 NavigationStack inside a TabView?


I would like to be able to popToRoot from my first tabItem (when im on page2 (from first tabItem) and tap twice on first tabItem when im on second tabItem) without having to use the NavigationViewKit package and only the new NavigationStack, is that possible?

My solution works fine but I would like to get rid of the NavigationViewKit package.

Any good advice\code example will be appreciated.

My code :

Home page

import SwiftUI
import NavigationViewKit

struct Home_V: View {
    @State var tabSelected: Int = 0
    @State private var tappedTwice: Bool = false
    
    // https://designcode.io/swiftui-handbook-tabbar-to-root-view
    var handler: Binding<Int> { Binding(
        get: { tabSelected },
        set: {
            if $0 == tabSelected {
                // print("tabSelected == \(tabSelected)")
                tappedTwice = true
            }
            tabSelected = $0
        }
    )}
    
    // https://github.com/fatbobman/NavigationViewKit
    @Environment(\.navigationManager) var nvmanager
    
    var body: some View {
        TabView(selection: handler) {
            NavigationStack {
                Page1()
                    .onChange(of: tappedTwice, perform: { tappedTwice in
                        guard tappedTwice else { return }
                        if tabSelected == 0 {
                            self.tappedTwice = false
                           nvmanager.wrappedValue.popToRoot(tag:"Page1", animated: true){}
                        }
                    })
            }
            .tabItem {
                Label("_HomeTitle", systemImage: "house")
                    .environment(\.symbolVariants, tabSelected == 0 ? .fill : .none)
            }
            .tag(0)
            .navigationViewStyle(StackNavigationViewStyle())
            
            NavigationStack {
                Page2()
            }
            .tabItem {
                Label("_MessagesTitle", systemImage: "envelope")
                    .environment(\.symbolVariants, tabSelected == 1 ? .fill : .none)
            }
            .tag(1)
            .navigationViewStyle(StackNavigationViewStyle())
            
        }

    }
}

Page1

import SwiftUI
import NavigationViewKit

struct Page1: View {
    var body: some View {
        VStack {
            Text("Page 1")
            NavigationLink {
                Page2()
            } label: {
                Text("Go to Page 2")
            }
        }
        .navigationViewManager(for: "Page1", afterBackDo: {print("Back to Page1")})
    }
}


Solution

  • I got it!

    Here is my test code :

    import SwiftUI
    
    class NavigationCoordinator: ObservableObject {
        @Published var path = NavigationPath()
        
        func popToRoot() {
            path.removeLast(path.count)
        }
    }
    
    struct Test_PopToRoot_NavigationStack: View {
        @State private var tabSelected: Int = 0
        @State private var tappedTwice: Bool = false
        
        @StateObject var navigationCoordinator = NavigationCoordinator()
        @StateObject var navigationCoordinator2 = NavigationCoordinator()
        
        
        // https://designcode.io/swiftui-handbook-tabbar-to-root-view
        var handler: Binding<Int> { Binding(
            get: { tabSelected },
            set: {
                if $0 == tabSelected {
                    // print("tabSelected == \(tabSelected)")
                    tappedTwice = true
                }
                tabSelected = $0
            }
        )}
        
        var body: some View {
            TabView(selection: handler) {
                NavigationStack(path: $navigationCoordinator.path) {
                    VStack {
                        NavigationLink(value: 1) {
                            Test_PopToRoot_Tabview1()
                                .foregroundColor(.black)
                                .onChange(of: tappedTwice, perform: { tappedTwice in
                                    guard tappedTwice else { return }
                                    if tabSelected == 0 {
                                        self.tappedTwice = false
                                        print("Home tapped twice!!!")
                                        navigationCoordinator.popToRoot()
                                    }
                                })
                        }
                    }
                }
                .environmentObject(navigationCoordinator)
                .tabItem {
                    Label("_HomeTitle", systemImage: "house")
                        .environment(\.symbolVariants, tabSelected == 0 ? .fill : .none)
                }
                .tag(0)
                .navigationViewStyle(StackNavigationViewStyle())
                
                NavigationStack(path: $navigationCoordinator2.path) {
                    VStack {
                        NavigationLink(value: 1) {
                            Test_PopToRoot_Tabview2()
                                .foregroundColor(.black)
                                .onChange(of: tappedTwice, perform: { tappedTwice in
                                    guard tappedTwice else { return }
                                    if tabSelected == 1 {
                                        self.tappedTwice = false
                                        print("2nd Tab tapped twice!!!")
                                        navigationCoordinator2.popToRoot()
                                    }
                                })
                        }
                    }
                }
                .environmentObject(navigationCoordinator2)
                .tabItem {
                    Label("_MessagesTitle", systemImage: "envelope")
                        .environment(\.symbolVariants, tabSelected == 1 ? .fill : .none)
                }
                .tag(1)
                .navigationViewStyle(StackNavigationViewStyle())
            }
        }
    }
    
    
    struct Test_PopToRoot_Tabview1: View {
        var body: some View {
            VStack {
                   NavigationLink(value: 2) {
                       Text("Go To Page2")
                           .foregroundColor(.black)
                   }
               }
               .navigationDestination(for: Int.self) { i in
                   Test_PopToRoot_Page2()
               }
            .navigationTitle(Text("Tabview1"))
        }
    }