TigerGold
TigerGold

Reputation: 193

Why doesn't calling method of child view from parent view update the child view?

I'm trying to call a method of a child view which includes clearing some of its fields. When the method is called from a parent view, nothing happens. However, calling the method from the child view will clear its field. Here is some example code:

struct ChildView: View {
    @State var response = ""
    
    var body: some View {
        TextField("", text: $response)
    }
    
    func clear() {
        self.response = ""
    }
}

struct ParentView: View {
    private var child = ChildView()
    
    var body: some View {
        HStack {
            self.child
            Button(action: {
                self.child.clear()
            }) {
                Text("Clear")
            }
        }
    }
}

Can someone tell me why this happens and how to fix it/work around it? I can't directly access the child view's response because there are too many fields in my actual code and that would clutter it up too much.

Upvotes: 1

Views: 242

Answers (2)

Asperi
Asperi

Reputation: 257493

SwiftUI view is not a reference-type, you cannot create it once, store in var, and then access it - SwiftUI view is a struct, value type, so storing it like did you work with copies it values, ie

struct ParentView: View {
    private var child = ChildView()     // << original value
    
    var body: some View {
        HStack {
            self.child                  // created copy 1
            Button(action: {
                self.child.clear()      // created copy 2
            }) {

Here is a correct SwiftUI approach to construct parent/child view - everything about child view should be inside child view or injected in it via init arguments:

struct ChildView: View {
    @State private var response = ""
    
    var body: some View {
        HStack {
            TextField("", text: $response)
            Button(action: {
                self.clear()
            }) {
                Text("Clear")
            }
        }
    }
    
    func clear() {
        self.response = ""
    }
}

struct ParentView: View {
    var body: some View {
        ChildView()
    }
}

Upvotes: 2

Abizern
Abizern

Reputation: 150565

Try using @Binding instead of @State. Bindings are a way of communicating state changes down to children.

Think of it this way: @State variables are used for View specific state. They are usually made private for this reason. If you need to communicate anything down, then @Binding is the way to do it.

struct ChildView: View {
    @Binding var response: String

    var body: some View {
        TextField("", text: $response)
    }

}

struct ParentView: View {
    @State private var response = ""

    var body: some View {
        HStack {
            ChildView(response: $response)
            Button(action: {
                self.clear()
            }) {
                Text("Clear")
            }
        }
    }

    private func clear() {
        self.response = ""
    }
}

Upvotes: 0

Related Questions