I have a fairly common set up:
ForEach
componentThe problem is that updating the underlying item (which is a struct) causes SwiftUI to automatically navigate backward. I assume this is because the struct is an immutable value and gets destroyed during the update, however, it does conform to Identifiable
so I'm expecting SwiftUI to understand that the item still exists and just needs to be updated rather than destroyed.
Is there any way to update the underlying list without navigating away from the detail view?
Here's a minimal, reproducible example.
import SwiftUI
struct ContentView: View {
var body: some View {
DemoList(viewModel: ViewModel())
}
}
struct DemoItem: Codable, Hashable, Identifiable {
var id: UInt
var description: String
}
final class ViewModel: ObservableObject, Identifiable {
@Published var list = [
DemoItem(id: 1, description: "One"),
DemoItem(id: 2, description: "two")
]
/// This update causes SwiftUI to automatically navigate away from the detail view
func update(item: DemoItem) {
list = list.map { $0.id == item.id ? item : $0 }
}
}
struct DemoList: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
NavigationView {
ForEach(viewModel.list, id: \.self) { item in
NavigationLink(destination: DemoDetail(viewModel: self.viewModel, item: item)) {
Text(item.description)
}
}
}
}
}
struct DemoDetail: View {
@ObservedObject var viewModel: ViewModel
var item: DemoItem
var body: some View {
Text(item.description)
.onTapGesture {
let newItem = DemoItem(id: self.item.id, description: UUID().uuidString)
self.viewModel.update(item: newItem)
}
}
}
Just remove the id from the ForEach and it'll stop navigating back
ForEach(viewModel.list) { item in
NavigationLink(destination: DemoDetail(viewModel: self.viewModel, item: item)) {
Text(item.description)
}
}