I am currently investigating converting a large macOS, iOS, and iPadOS app from using AppKit and UIKit for the front end to using SwiftUI. This app uses a sidebar/source list style window on macOS and iPadOS. The content/detail portion of the window displays hierarchal information so you can drill in for details and back out to get an overview.
In this application the selection in the sidebar changes how the information in the content/detail area is displayed but not what information is displayed. The included ContentView SwiftUI code shows an example of this. The sidebar lets you change the background color of the detail view and the content of detail view provides all of the "links" to navigate forward to see more data.
To try and make this work I am using a NavigationSplitView with a List for the sidebar and a NavigationStack for the detail. The sidebar list takes a selection binding and the list contents uses tags to map the selection. It does not use NavigationLink and the selection state variable is not used in the NavigationStack of the detail view. With this setup, I would expect there to be no relationship between the sidebar and detail views.
The problem is whenever the selection in the list in the sidebar changes the NavigationStack in the details backs up to its root view. I have included an example ContentView showing this behavior.
Is there anyway to prevent this? Should I file a "feedback" to request this functionality?
Thank you in advance for any help.
@State
var path: [ContentView.Step] = []
@State
var color: Color? = .clear
var body: some View {
NavigationSplitView {
List (selection: $color) {
Text ("Clear").tag(Color.clear)
Text ("White").tag(Color.white)
Text ("Blue").tag(Color.blue)
Text ("Green").tag(Color.green)
}
} detail: {
NavigationStack (path: $path) {
VStack {
NavigationLink(value: ContentView.Step.a) {
Text("Push A")
}
NavigationLink(value: ContentView.Step.b) {
Text("Push B")
}
NavigationLink(value: ContentView.Step.c) {
Text("Push C")
}
Image(systemName: "globe")
.imageScale(.large)
Text("Hello, world!")
}
.navigationDestination(for: ContentView.Step.self) { step in
step.body
.toolbar {
ToolbarItemGroup(placement: .automatic) {
Button {
_ = path.popLast()
} label: {
Text("Back")
}
.disabled (path.count <= 0)
}
}
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(color)
}
}
enum Step: Hashable {
case a
case b
case c
var body: some View {
Group {
switch self {
case .a:
VStack {
Text ("A")
NavigationLink(value: ContentView.Step.b) {
Text("Push B")
}
NavigationLink(value: ContentView.Step.c) {
Text("Push C")
}
Image(systemName: "globe")
.imageScale(.large)
.hidden()
Text("Hello, world!")
}
case .b:
VStack {
NavigationLink(value: ContentView.Step.a) {
Text("Push A")
}
Text ("B")
NavigationLink(value: ContentView.Step.c) {
Text("Push C")
}
Image(systemName: "globe")
.imageScale(.large)
Text("Hello, world!")
.hidden()
}
case .c:
VStack {
NavigationLink(value: ContentView.Step.a) {
Text("Push A")
}
NavigationLink(value: ContentView.Step.b) {
Text("Push B")
}
Text ("C")
Image(systemName: "globe")
.imageScale(.large)
.hidden()
Text("Hello, world!")
.hidden()
}
}
}
}
}
}