Persist navigation paths in NavigationSplitView with NavigationStacks

I have an SwiftUI iOS app that uses TabView to display 4 different NavigationStacks. Each stack can be navigated into. To make this app work better on iPad OS I'd like to use a NavigationSplitView with a sidebar and a detail view. The sidebar contains a menu with the 4 items that are tabs on iOS. The detail view should contain individual NavigationStacks that should retain their view paths.

After having played around with it a bit it seems to me that NavigationSplitView is not meant to be used that way. The detail view resets every time I select another menu item in the sidebar. I've tried retaining the individual navigation paths (as @States) in my root view and passing them into the individual NavigationStack when creating the detail view. However, it seems like NavigationSplitView is resetting the path whenever you switch to another menu item.

Has anybody figured this out?

Post not yet marked as solved Up vote post of seboslaw Down vote post of seboslaw
399 views

Replies

Hi @seboslaw could you provide a sample code snippet or a focused sample project that reproduces the issue? Thanks

I think I have the same issue with the following view.

  1. Use the sidebar to navigate to "Item home"
  2. Use a navigation link to go to "Nav Test 1"
  3. Use the sidebar to navigate to "Item market"
  4. Use a navigation link to go to "Nav Test 2"
  5. Use the sidebar to navigate back to "Item home"

Note the path is not the same as when you navigated away from the original selection in step 3.


import SwiftUI

struct NavBug1: View {

    @State var selection: Item?
    @State var paths: [Item: NavigationPath] = [:]

    var body: some View {
        NavigationSplitView {
            List(Item.allCases, selection: $selection) { item in
                Text("Item \(item.rawValue)")
            }
        } detail: {
            if let selection {
                NavigationStack(path: path(for: selection)) {
                    List {
                        Text("Item \(selection.rawValue)")
                        NavigationLink("Nav Test 1", value: Nav1.test1)
                        NavigationLink("Nav Test 2", value: Nav1.test2)
                    }
                    .navigationTitle("Item \(selection.rawValue)")
                    .navigationDestination(for: Nav1.self) { nav1 in
                        Text("Nav Item \(nav1.rawValue)")
                            .navigationTitle("Nav Item \(nav1.rawValue)")
                    }
                }
            }
        }
    }

    func path(for item: Item?) -> Binding<NavigationPath> {
        Binding {
            if let selection, let path = paths[selection] {
                path
            } else {
                .init()
            }
        } set: { newValue in
            if let selection {
                paths[selection] = newValue
            }
        }
    }

    enum Item: String, Equatable, Hashable, Identifiable, CaseIterable {
        case home
        case market

        var id: Self { self }
    }

    enum Nav1: String, Equatable, Hashable, Identifiable, CaseIterable {
        case test1
        case test2

        var id: Self { self }
    }
}