I am new to SwiftUI. I have an object that has various properties that I want to bind to fields in the UI. However, the UI is dynamically created. As in, the fields to include and what order can be customized.
Having trouble getting the UI to refresh when the object is updated (for example, when the user enters a zip code, we automatically populate the city and state fields) I have created a simplified sample.
I can get it to work by handling each individual field when creating the TextField
, but would like to see if it is possible to be more generic.
Here is the sample object that the UI is displaying the properties
class TestObject: ObservableObject {
@Published var testForeignKeyId: Int = 0
@Published var city: String = ""
@Published var state: String = ""
@Published var zip: String = ""
func getStringValue(field: String) -> String {
switch field {
case "City":
return self.city
case "State":
return self.state
case "Zip":
return self.zip
default:
return ""
}
}
func getIntValue(field: String) -> Int {
switch field {
case "TestForeignKeyId":
return self.testForeignKeyId
default:
return 0
}
}
}
Sample field struct:
struct TestField: Identifiable {
let id = UUID().uuidString
var label: String
var fieldType: String
}
It works if I do this and handle each field:
struct ContentView: View {
@ObservedObject var obj: TestObject
var fields: [TestField] = []
@State var stringValue = ""
init() {
self.fields = [TestField(label: "City", fieldType: "Text"), TestField(label: "State", fieldType: "Text"), TestField(label: "Zip", fieldType: "Zip")]
self.obj = TestObject()
}
var body: some View {
List {
ForEach(self.fields) { field in
Text(field.label)
switch field.label {
case "City":
TextField("", text: $obj.city)
case "State":
TextField("", text: $obj.state)
case "Zip":
TextField("", text: $obj.zip,
onEditingChanged: { edit in
if !edit {
// lookup values for zip
self.obj.city = "Test"
self.obj.state = "TT"
}
})
default:
Text(field.label)
}
}
}
}
}
Would like to be able to do something like this and create the text fields based on the fieldtype
instead of the individual fields (since the actual object has many more properties):
switch field.fieldType {
case "Text":
Text(field.label)
TextField("", text: self.obj.getStringValue(field: field.label))
case "Zip":
Text(field.label)
TextField("", text: self.obj.getStringValue(field: field.label))
}
But when I do that, I get the error:
Cannot convert value of type String
to expected argument type Binding<String>
I also tried using a state variable like
@State var stringValue = ""
Then in the.onAppear of the text field, I assigned the value of the appropriate property ( obj.state
etc) to the stringValue
state variable. I thought that maybe the control would refresh if the observedObject
was updated, but couldn't figure out where to update the stringValue
state variable to get it working.
Hopefully I explained what I'm trying to do well enough. Does anyone have an idea on how to get it working?
Thanks
Well the problem with getStringValue(field: String)
function is that it returns a String
value (constant) while SwiftUI TextField
View takes a Binding
for its text
parameter.
So, SwiftUI
is throwing that error because it's not able to convert automatically a String
value to a Binding
of that string. One way to solve this is to move your getStringValue(field: String)
function into your ContentView
and instead of returning a String
, you should return a Binding
of the string.
So, the function would look like this:
func getStringValue(field: String) -> Binding<String> {
switch field {
case "City":
return self.$obj.city
case "State":
return self.$obj.state
case "Zip":
return self.$obj.zip
default:
return .constant("")
}
}
At this point, you should not have that error again and the code should compile correctly.
Of course, you can still improve this by using a Struct
and enum
instead of a hard-coded
string values.
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.