I've looked around but haven't seen any info on this. If I want to have multiple navigation stacks in SwiftUI, and have multiple navigation links that add to different navigation stacks, how can I do this? In my use case I use a tab view, so I would be something like:
NavigationStack(path: $globalNavigationStack) {
TabView {
PrimaryView()
SecondaryView()
}
}
Then maybe in PrimaryView:
struct PrimaryView: View {
var body: some View {
NavigationStack(path: $localNavigationStack) {
NavigationLink(value: SomeType.self) {
Button("This should add to local stack")
}
.navigationDestination(for: SomeType.val) { someType in
ViewOnLocalStack()
}
NavigationLink(value: SomeType.self) {
Button("This should add to global stack")
}
.navigationDestination(for: SomeType.val) { someType in
ViewOnGlobalStack()
}
}
}
}
But the second NavigationLink will add to the local stack. Assuming I have access to the global path, how can I make the second one add to global path? What I tried was this:
NavigationStack(path: $globalNavigationStack) {
TabView {
PrimaryView()
SecondaryView()
}
}
.navigationDestination(for: SomeType.val) { someType in
ViewOnGlobalStack()
}
along with removing the onDestination from the second link, but the console complained that the navigation link had no corresponding onDestination. Not sure if I've seen anyone else have a similar situation.
TLDR; Nested NavigationStack isn't possible. You'll need a mixed approach. The following answer is advice on how to approach it assuming nesting is not possible.
XCode will not necessarily complain if your try. But even if you're able to make it work your app will quickly crash.
My advice: don't try to hack it. NavigationStack is decent at managing navigation state at one level deep. To create nuance in your view states, you will either have to create numerous views with isolated state or fewer robust views with shared state.
Here is the bad approach versus some suggested approaches in tree structure. Note that I am using the term "screen" because view has a specific meaning in SwiftUI.
How you're currently imagining your app navigation scheme:
App <- NavigationStack
│
├── Screen1 <- NavigationStack
│ ├── Subscreen1A
│ └── Subscreen1B
│
└── Screen2 <- NavigationStack
├── Subscreen2A
└── Subscreen2B
App <- NavigationStack and global state object for subviews.
│
├── Screen1 <- reacts to global state object for changes
├── Screen2 <- reacts to global state object for changes
App <- NavigationStack
│
├── Screen1 <- custom state object with conditionally rendered subviews
├── Screen2 <- custom state object with conditionally rendered subviews
So I'll note a few points of guidance for how to approach subviews:
NavigationStack, use these data types to determine how to render the entire view or parts of your view.Lastly, here's an example to illustrate the "better approach" I mentioned earlier. It'll get you started assuming a global state works for now.
import SwiftUI
enum AppRoute: Hashable {
case screenOne
case screenTwo
case screenThree
}
final class AppState: ObservableObject {
@Published var path = NavigationPath()
// Can't be optional.
// Manage in parent to inject state into "child" views
@Published var screenTwoData = ?
@Published var isScreenTwoFlowActive: Bool = false
}
@main
struct MyApp: App {
@StateObject private var appState = AppState()
var body: some Scene {
WindowGroup {
NavigationStack(path: $appState.path) {
VStack {
ScreenOne()
}
.navigationDestination(for: AppRoute.self) { appRoute in
switch appRoute {
case .screenOne:
ScreenTwo()
case .screenTwo:
ScreenThree()
}
}
}
.environmentObject(appState)
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With