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

Time:03-08

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)
                    }
                }
            }
        }
    }
}
  • Related