简体   繁体   中英

Why does custom Binding not update SwiftUI TextField?

I feel like I'm misunderstanding how Binding works in SwiftUI, in that I do not understand why the first TextField below does not update when the text view model property changes:

class MyViewModel: ObservableObject {
    @Published var text: String = "Hello"
    
    var binding: Binding<String>!

    init() {
        binding = Binding(get: { self.text }, set: { self.text = $0 })
    }
}

struct MyView: View {
    @StateObject var viewModel = MyViewModel()
    
    var body: some View {
        VStack {
            Text("Actual: " + viewModel.text)
            TextField("Field 1", text: viewModel.binding)
            TextField("Field 2", text: $viewModel.text)
        }
    }
}

struct MyView_Previews: PreviewProvider {
    static var previews: some View {
        MyView()
    }
}

Typing in the first TextField always updates second one (and the Text label), but typing in the second TextField only propagates changes to the Text label, shown here:

模拟器截图

In my actual use case, the list of TextField views is dynamically generated from a user-defined template, likewise with custom bindings to a @Published complex object (ie not as simple as a String ) on MyViewModel . In my limited experience, I believe the dynamic nature of the view prevents me from coding it in a straight-forward fashion, as seen with the second TextField .

My guess is that SwiftUI has no insight that the custom binding should even need to be refreshed, in which case my question becomes: How can I tell the binding to "refresh" the TextField while also supporting dynamically-generated content?

First, consider this line code

binding = Binding(get: { self.text }, set: { self.text = $0 })

You are assigning binding value to self.text by self.text = $0 , it means now self.text value updated and your view is rendering with new value so, it will display with new value in the all place.

Now, you are changing the value of @Published var by the second field. when you are changing this var, your just changing the value of @Published var text: String this var and it's not related to this binding var var binding: Binding<String>! there is no communication or connection. so var binding: Binding<String>! value is not changed and remains the same as the old value.

If you want to update both directions, you also need to add this code

@Published var text: String = "Hello" {
        didSet {
            binding = Binding(get: { self.text }, set: { self.text = $0 }) // <-- Here
        }
    }

So now it will reflect on both sides.


If you have still confusion Replace this line

var binding: Binding<String>!

with this

@Published var binding: String = ""

And now think. The above way is the same way as your code, just have a different declaration. These are now two different var but doing the same things.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM