I'm trying to get a NavigationView
to pre select an initial view but it seems impossible to get it working across both iPhone and iPad.
Here's a very basic model with 3 views:
struct SomeView: Hashable {
let title: String
let view: Text
func hash(into hasher: inout Hasher) {
hasher.combine(title)
}
}
let destinations = [
SomeView(title: "View0", view: Text("View0")),
SomeView(title: "View1", view: Text("View1")),
SomeView(title: "View2", view: Text("View2"))
]
and here's a very simple navigation view:
struct ContentView: View {
@State var initialSelection: SomeView? = destinations[0]
var body: some View {
NavigationView {
SideBar(selected: $initialSelection)
}
}
}
struct SideBar: View {
@Binding var selected: SomeView?
var body: some View {
List {
ForEach(destinations, id: \.self) { d in
NavigationLink(d.title, destination: d.view, tag: d, selection: $selected)
}
}
}
}
When launched on an iPhone 13 Pro Max in Portrait
mode, it correctly shows View0
. When launched in Landscape
mode, we get a blank screen with a back button.
So it appears that the selection
property of the NavigationLink
will only work if that NavigationLink
is drawn on the screen. I guess that makes sense.
We can fix it by adding our default view to the NavigationView
stack like this:
var body: some View {
NavigationView {
SideBar(selected: $initialSelection)
initialSelection?.view
}
}
Now in Landscape
mode we get the View0
showing. However when we tap the back button, nothing happens on the first tap. This is because tapping back, loads up the List
view which triggers the selection
property to show our default view. Tapping back for a second time, works.
I tried to fix this using some logic to know if the NavigationView
had actually shown the second column view or not, however it seems that even on an iPhone in portrait when it displays as a stack, the .onAppear
code (shown below) still fires because the view was loaded and the logic is pointless.
struct ContentView: View {
@State var initialSelection: SomeView?
@State var secondColumnShown = false
var body: some View {
NavigationView {
SideBar(selected: $initialSelection)
initialSelection?.view
.onAppear(perform: {
secondColumnShown = true
})
}
.navigationViewStyle(.columns)
}
}
Is there a way to force the draw open for it to trigger the selection
of the NavigationLink
?
Or is there some better logic I can use to only set the initialSelection
view if
it's on a small screen and the draw hasn't been shown?
I've been battling this for a long time, and there's many stack overflow questions about this but no solutions that I can get to work on iOS 15