SwiftUI NavigationView initial selection (iOS 15)

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

Post not yet marked as solved Up vote post of dazboj Down vote post of dazboj
1.4k views