Есть ли у кого-нибудь работающий NavigationSplitView с NavigationStack в подробном представлении?

Независимо от того, как он настроен, сложный трехколоночный NavigationSplitView с NavigationStack дает сбой или имеет противоречивый рабочий процесс.

  • Выбор боковой панели определяет, какой контент (или функция) находится в разделе «Контент».

  • Контент (или функция), который доступен (или выбран), может контролировать детали.

  • Подробности должны быть Navigable, а NavigationDestinations зависят от каждой функции (выбранного контента), поэтому их нельзя зарегистрировать все под одним корнем.

Варианты, которые я пробовал: (и многие другие)

      @State var sideBar: SideBar?
@State var content: Content?
NavigationSplitView(
    sidebar: {
        List(sidebar) { ... }
    },
    content: {
        switch sidebar { ... }
    },
    detail: {
        XXX what to put here?
    }
).navigationSplitViewStyle(.balanced)

Каждая боковая панель содержит набор маршрутов, который зависит от нее. Итак, подробно: я пробовал несколько вариантов.

      // this does not work as NavigationStack is not simple
// enough to be completely removed or recreated
// somehow it ties on the Scene and crashes when
// switching between sidebars
detail: {
    switch sidebar {
    case .a:
        NavigationStack {
            View()
            .navigationDestination(...
        }
    }
}
      // clearing the navigationPath just before switching
// sidebars, but still controlling a NavigationStack
// inside details does not work, nothing happens.
detail: {
    switch sidebar {
    case .a:
        NavigationStack(navigationPath) {
            View()
            .navigationDestination(...
        }
    }
}
      // Having an empty NavigationStack in details
// but registering the destinations inside the content.
// This works however, when switching between sidebar
// it also crashes sometimes or it shows the Warning triangle
// on the navigation stack, as destinations become unavailable.
content: {
    switch sidebar {
    case .a:
        RootView()
            .navigationDestination(...)
    }
},
detail: {
    NavigationStack { }
}
      // Trying to clear out the path before switching
// does not work either.
content: {
    switch sidebar {
    case .a:
        RootView()
            .navigationDestination(...)
    }
},
detail: {
    NavigationStack(navigationPath) { }
}

1 ответ

После долгих отладок я нашел способ, который работает.

Размещение одного NavigationStack в представлении Details и сохранение где-нибудь NavigationPath. При переключении между боковой панелью мне пришлось очистить путь, прежде чем разрешить NavigationSplitView обновить себя и свое содержимое.

Мне также пришлось установить определенный .id() для представления содержимого, которое является боковой панелью, каким-то образом, даже когда боковая панель изменяется, содержимое не обновляется.

Я тестировал это на iPhone 16.4, iPad и MacOs.

      // in StateObject, Model or wherever
var selectedSidebar: SomeSidebarType? {
    willSet {
        if navigationPath.isEmpty {
            // if the stack is empty, we do not need to clear it
            objectWillChange.send()
        } else {
            // otherwise clear it, and do not send an update
            // as the stack will update the view and itself
            stack.removeLast(stack.count)
        }
    }
}

@ViewBuilder
var detail: some View {
    NavigationStack(path: $storage.navigationPath) {
        switch storage.selectedSidebar {
        case .a:
            SomeView
        case .b:
            SomeView
        default:
            Text("No Selection")
        }
    }
}