I am having trouble understanding why SwiftUI list selection behaviour is different between the Previews and the actual app running when pushing a detail view.
Here is the app view
import SwiftUI
@main
struct SwiftUIListApp: App {
var body: some Scene {
WindowGroup {
ListView()
}
}
}
and here is my simple list
import SwiftUI
struct ListItem: Identifiable, Hashable {
let title: String
var id: String { title }
}
@Observable
final class ListViewModel {
private(set) var items: [ListItem] = []
@MainActor func loadData() async {
items = (1...100).map { ListItem(title: "\($0)") }
}
}
struct ListView: View {
@State private var viewModel = ListViewModel()
var body: some View {
NavigationStack {
List {
ForEach(viewModel.items) { item in
NavigationLink(value: item) {
Text(item.title)
}
}
}
.navigationTitle("SwiftUI List")
.navigationDestination(for: ListItem.self) { item in
ListDetailView(item: item)
}
}
.task {
await viewModel.loadData()
}
}
}
struct ListDetailView: View {
let item: ListItem
var body: some View {
Text("Detail view \(item.title)")
.navigationTitle("Detail")
.navigationBarTitleDisplayMode(.inline)
}
}
#Preview {
ListView(viewModel: ListViewModel())
}
When I select a row in the preview, the row gets selected and the detail view is pushed. When I navigate back to the list the row gets de-selected when the list view appears. This is similar behaviour to UITableViewController and is exactly what I want. (https://developer.apple.com/documentation/uikit/uitableviewcontroller/1614758-clearsselectiononviewwillappear)
However when I run the app in the simulator/device, the row gets deselected immediately before the push. The list view itself does not seem to re-draw because scroll position is maintained correctly.
Found the solution after playing around with Apples demo app https://developer.apple.com/documentation/swiftui/bringing_robust_navigation_structure_to_your_swiftui_app
Added this to the view model
var itemPaths: [ListItem] = []
And updated the NavigationStack
NavigationStack(path: $viewModel.itemPaths) { ... }
Still not sure why the preview behaves differently but I take it.