skip to Main Content

I have recently updated the navigation in my app from using lots of NavigationLinks, because I was seeing errors surrounding multiple pushes to the navigation stack.

My app is now working well with a custom navcontroller that programatically adds views to the stack, but there is one issue with when I try to pop back to the previous view using the back button on the toolbar it always pops all the way back to the root going through all the child views one by one.

Here is an overview of the current setup:

Navigation Controller –

class NavigationController: ObservableObject {

@Published var homeNavPath: [AppView] = []

public func navigateTo(destination: AppView) {
      homeNavPath.append(destination)
}

}

Navigation Stack –

 NavigationStack(path: $navController.homeNavPath) {
     HomeView()
 }

Navigation Example –

public struct HomeView: View {

    @EnvironmentObject var navController: NavigationController

    public var body: some View {
        Text("GO TO POST").onTapGesture {
            navController.navigateTo(PostView("123"))
        }
    }
}


public struct PostView: View {

    @EnvironmentObject var navController: NavigationController

    public var body: some View {
        Text("GO TO USER").onTapGesture {
            navController.navigateTo(UserView("123"))
        }
    }
}

public struct UserView: View {

    @EnvironmentObject var navController: NavigationController

    public var body: some View {
        Text("USER INFORMATION...")
    }
}

I am able to perfectly navigate from Home -> Post -> User as expected, but when I press the back button on the user page, it will always navigate back to the root (e.g home in this case) no matter how long the navigation path it.

Here is an example, I only press the back button once here:

example navigation gif

Let me know if there is any more information that could help and thanks for any suggestions

2

Answers


  1. Chosen as BEST ANSWER

    I took a while but I found the solution.

    The issue was where I was declaring my navigationDestination.

    I had the following setup (I had simplified it even more in my question).

        private let homeView = HomeView()   
        NavigationStack(path: $navController.homeNavPath) {
          homeView
        }
    
        public struct HomeView: View {
    
        @EnvironmentObject var navController: NavigationController
    
        public var body: some View {
            Text("GO TO POST").onTapGesture {
                navController.navigateTo(PostView("123"))
            }.navigationDestination { ... }
        }
    }
    
    

    There seems to be two ways to fix this. Either I need to call HomeView() directly in the NavigationStack (but I am sure I was not doing that originally for some reason...) or I can move the navigationDestiation{} call to be like either of these. All of these fix the issue, although I'm not 100% sure how:

        private let homeView = HomeView()   
        NavigationStack(path: $navController.homeNavPath) {
          homeView.navigationDestiation{...}
        }
    

    or

        private let homeView = HomeView().navigationDestiation{...} 
        NavigationStack(path: $navController.homeNavPath) {
          homeView
        }
    

    If anyone has an explanation for why this works that'd be appreciated


  2. I’m unsure what your AppView enum looked like, but here’s a working version of the code you provided.

    A quick note: I would suggest you move NavigationPath directly into whichever view owns the NavigationStack as a @State property (that way you can use NavigationLinks and not have to pass navController through environmentObject modifiers).

    @main
    struct StackDeleteApp: App {
        var body: some Scene {
            WindowGroup {
                HomeCoordinatorView()
            }
        }
    }
    
    enum AppView: Hashable {
        case postView(String)
        case userView(String)
    }
    
    class NavigationController: ObservableObject {
        @Published var homeNavPath = NavigationPath()
    
        func navigateTo(_ destination: AppView) {
            homeNavPath.append(destination)
        }
    }
    
    struct HomeCoordinatorView: View {
        @StateObject var navController = NavigationController()
    
        var body: some View {
            NavigationStack(path: $navController.homeNavPath) {
                 HomeView()
                    .environmentObject(navController)
                    .navigationDestination(for: AppView.self) { appView in
                        switch appView {
                        case .postView(let post):
                            PostView(post: post)
                                .environmentObject(navController)
                        case .userView(let user):
                            UserView(user: user)
                                .environmentObject(navController)
                        }
                    }
             }
        }
    }
    
    struct HomeView: View {
        @EnvironmentObject var navController: NavigationController
    
        var body: some View {
            Text("GO TO USER").onTapGesture {
                navController.navigateTo(.userView("123"))
            }
        }
    }
    
    struct PostView: View {
        let post: String
        @EnvironmentObject var navController: NavigationController
    
        var body: some View {
            Text("GO TO USER").onTapGesture {
                navController.navigateTo(.userView("123"))
            }
        }
    }
    
    struct UserView: View {
        let user: String
        @EnvironmentObject var navController: NavigationController
    
        var body: some View {
            Text("GO TO POST").onTapGesture {
                navController.navigateTo(.postView("123"))
            }
        }
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search