I have a protocol in which implementors must have a static function which creates a view. The view then creates an object and places in into the binding location which was supplied.
protocol Creatable {
associatedtype CreationView: View
static func buildCreationView(toReplace: Binding<AnyMyObject>) -> CreationView
// ...
}
In practise there is a screen in which the user selects from a list of implementors and when that selection changes, buildCreationView creates a form for all the required properties of said object and a create button which write the new object into toReplace.
When I implement buildCreationView however I need to be able to create state for this form. My naive implementation was just to use an @State property like so:
@ViewBuilder static func buildCreationView(toReplace: Binding<AnyMyObject>) -> some View {
@State var fieldOne: String = ""
Form {
TextField("Field One", text: $fieldOne) // Error here
Button {
toReplace.wrappedValue = ObjectOne(fieldOne: fieldOne).erase
} label: {
Text("Create Object One")
}
}
}
This create a swiftUI runtime error saying: Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update.
Not knowing how to fix this I tried a hack of:
var propertyOne: String = ""
let propertyOneBinding: Binding<String> = .init {
propertyOne
} set: {
propertyOne = $0
}
And using the binding with the TextField. Which does work but is very ugly.
What is the correct way to house @State within a view building function?
CodePudding user response:
Create a View
-conforming type as your CreationView
, and move your @State
property into it.
struct MyCreatable: Creatable {
static func buildCreationView(toReplace: Binding<AnyMyObject>) -> CreationView {
return CreationView(toReplace: toReplace)
}
struct CreationView: View {
@Binding var toReplace: AnyMyObject
@State var fieldOne: String = ""
var body: some View {
Form {
TextField("Field One", text: $fieldOne)
Button {
toReplace = ObjectOne(fieldOne: fieldOne).erase
} label: {
Text("Create Object One")
}
}
}
}
}