My goal is to have an array of structs that when tapped on an individual item, a change is made and passed up to the parent view but doesn't automatically dismiss my child view. I'm really not sure why the child view is automatically dismissing and how to prevent it. Here is my code for the ContentView
import SwiftUI
struct Person: Hashable {
var name: String = ""
}
struct ContentView: View {
@State private var patients: [Person]
init() {
var temp: [Person] = []
for index in 0 ..< 3 {
temp.append(Person(name: "\(index)"))
}
patients = temp
}
var body: some View {
NavigationView {
VStack(spacing: 40) {
ForEach($patients, id:\.self) { $patient in
NavigationLink("\(patient.name)") {
ChangeNameView(patient: $patient)
}
}
}
}
}
}
struct ChangeNameView: View {
@Binding var patient: Person
var body: some View {
Button {
patient.name = "New Name"
} label: {
Text("Set New Name")
}
}
}
Tapping on a name will bring up the child view as expected. Then tapping the button to change the name does so, but then immediately dismisses the view.
CodePudding user response:
The problem comes from the fact that Person
is Hashable
(which only uses the name
property) and for your ForEach
, you're using \.self
for the id
.
SwiftUI's ForEach
uses that id:
parameter to keep track of which element is which in the list. Because you're using \.self
and then changing the name
of that element in ChangeNameView
, it no longer looks like the same element and SwiftUI removes the child view from the hierarchy (since the old one doesn't exist in the list anymore).
One solution is to use truly Identifiable
elements, where the id
is unique and doesn't update when you change the name
:
struct Person: Hashable, Identifiable { //<-- Here
var id = UUID() //<-- Here
var name: String = ""
}
struct ContentView: View {
@State private var patients: [Person]
init() {
var temp: [Person] = []
for index in 0 ..< 3 {
temp.append(Person(name: "\(index)"))
}
patients = temp
}
var body: some View {
NavigationView {
VStack(spacing: 40) {
ForEach($patients) { $patient in //<-- Here -- when the items are Identifiable, it's essentially like saying `id: \.id`
NavigationLink("\(patient.name)") {
ChangeNameView(patient: $patient)
}
}
}
}
}
}