Unable to set initial selection state of SwiftUI `List` in iOS 14

I'm using a NavigationView to display "split view" style sidebar and secondary views similar to the Fruta sample app. My sidebar has a List of NavigationLink's, and my secondary view displays that linked content. However, I'm not able to set an initial "default" selection (i.e. blue background selected row style) for the List in my sidebar. When the app first launches, there's no selection state in the List for the secondary view that is displayed until the user taps a row of the List. I have a @State var called selection that's passed into my Sidebar, which references it as a @Binding. selection has a value, which is why it's unexpected that the resulting List doesn't show it as selected when first displayed.

EXPECTED RESULTS
When the app launches, I would expect the List selects the initial row matching the @Binding that's passed in.

ACTUAL RESULTS
When the app launches, The List has no initial selected row until the user taps a row.

I'd like for the user to launch into the app with a Sidebar List row already selected, similar to the way the Shortcuts app does when launched. How do I enable this kind of behavior?

Here's full demo code – iOS 14 beta 4, iPad in landscape:
Code Block swift
import SwiftUI
@main
struct SidebarSelectionApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
enum NavigationItem: Int, Identifiable {
var id: Int { return self.rawValue }
case view1
case view2
case view3
}
struct ContentView: View {
@State var selection: NavigationItem? = .view1
var body: some View {
Sidebar(selection: $selection)
}
}
struct Sidebar: View {
@Binding var selection: NavigationItem?
var body: some View {
NavigationView {
list
EmptyView()
}
}
var list: some View {
List(selection: $selection) {
NavigationLink(
destination: View1(),
tag: NavigationItem.view1,
selection: $selection,
label: {
Text("View 1")
})
NavigationLink(
destination: View2(),
tag: NavigationItem.view2,
selection: $selection,
label: {
Text("View 2")
})
NavigationLink(
destination: View3(),
tag: NavigationItem.view3,
selection: $selection,
label: {
Text("View 3")
})
}
.listStyle(SidebarListStyle())
.navigationTitle("List")
}
}
struct View1: View {
var body: some View {
Text("This is View 1").font(.title).foregroundColor(.green)
}
}
struct View2: View {
var body: some View {
Text("This is View 2").font(.title).foregroundColor(.blue)
}
}
struct View3: View {
var body: some View {
Text("This is View 3").font(.title).foregroundColor(.red)
}
}



Post not yet marked as solved Up vote post of melt Down vote post of melt
2.4k views
  • No solution here. As far as I can tell, when SwiftUI instantiates or updates a List, it sets the bound selection variable to nil, and I have not been able to find any way to change this. For example, setting the selection variable in an .onAppear modifier on the list doesn't work, because SwiftUI apparently clears the selection after the onAppear action executes. I tried playing with the selection variable’s didSet property observer, but SwiftUI apparently clears the variable in a way that doesn’t trigger didSet. I’ve pretty much reached a dead end, too.

Add a Comment

Replies

Still trying to figure this one out...
Post not yet marked as solved Up vote reply of melt Down vote reply of melt

I just got this to work in my app by making the selection variable non-optional, e.g.

@State var selection: NavigationItem = .view1

Now the sidebar list shows up with the correct item highlighted. The initial state provided in the declaration can be overridden in the onAppear view modifier, i.e.

.onAppear { selection = .view2 }

Not sure this works if a nil selection state is a required option. Perhaps provide a .none enum case, and don't embed it in the list?

@State private var selectedCategoryId: MenuItem.ID? = .fileTab //  set default 

List(menuM.mainMenuItems, selection: $selectedCategoryId) { item in }