更改 @Binding 结构的值会导致子视图在父视图更新时被关闭

Changing the value of a struct that is @Binding causes the child view to be dismissed as the parent view updates

我的目标是拥有一组结构,当点击单个项目时,会进行更改并将其传递到父视图,但不会自动关闭我的子视图。我真的不确定为什么子视图会自动关闭以及如何防止它。这是我的 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")
        }
    }
}

点击名称将按预期调出子视图。然后点击按钮更改名称,但会立即关闭视图。

问题是因为 PersonHashable(它只使用 name 属性)并且对于你的 ForEach,你'重新使用 \.self 作为 id.

SwiftUI 的 ForEach 使用 id: 参数来跟踪列表中的哪个元素。因为您正在使用 \.self 然后 更改 ChangeNameView 中那个元素的 name,它看起来不再像同一个元素并且 SwiftUI 删除了层次结构中的子视图(因为旧视图不再存在于列表中)。

一个解决方案是使用真正的 Identifiable 元素,其中 id 是唯一的并且在您更改 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)
                    }
                }
            }
        }
    }
}