我正在尝试使用 SwiftUI 创建一个表单框架。现在,我正在保存我的 FormFieldModels 的字典,以保存有关该字段是否需要、它是什么类型的字段(文本、复选框、电话等)及其值的信息。我对 SwiftUI 的经验并不丰富,但我正在尝试将字段绑定到自定义文本字段视图/复选框(最终将是更多字段选项),并从那里使用包含在字段中的值来确定状态。
由于我不完全理解的原因,只有任何字段(无论是文本字段还是切换按钮)的第一次更新有效,然后其余字段不更新主 ContentView 的状态(尽管他们的新值被正确传递,只是没有坚持)。
编码:
import SwiftUI
enum FormFieldType: Hashable, Equatable {
case plainText
case checkbox
case ageGate
case phone
case picker([String])
}
struct FormFieldModel: Hashable {
//Constants
let title: String
let type: FormFieldType
let required: Bool
var value: Any?
func validate() -> Bool {
switch type {
case .phone:
return false
default:
return true
}
}
//MARK: Equatable Conformance
static func == (lhs: FormFieldModel, rhs: FormFieldModel) -> Bool {
return lhs.title == rhs.title
&& lhs.type == rhs.type
&& lhs.required == rhs.required
}
func hash(into hasher: inout Hasher) {
hasher.combine(title)
hasher.combine(type)
hasher.combine(required)
}
}
struct AddressViewModel {
let first:String
let last:String
}
struct ContentView: View {
enum FieldKey: String {
case first, last, enroll
}
var existingAddress: AddressViewModel?
@State var fields:[FieldKey:FormFieldModel] = [.first : .init(title: "First Name", type: .plainText,
required: true, value: nil),
.last : .init(title: "Last Name", type: .plainText,
required: true, value: nil),
.enroll : .init(title: "Enroll", type: .checkbox,
required: true, value: nil)]
{
didSet {
print("fields has been modified")
let v = fields[.first]?.value as? String ?? "none"
print("new value of first is \(v)")
}
}
var body: some View {
VStack {
FloatingTextField(model: binding(for: .first))
FloatingTextField(model: binding(for: .last))
CheckboxView(model: binding(for: .enroll))
Button("Print Values") {
printValues()
}
Spacer()
}
.padding()
}
func printValues() {
print("\(FieldKey.first.rawValue): \(fields[.first]?.value ?? "none")")
print("\(FieldKey.last.rawValue): \(fields[.last]?.value ?? "none")")
print("\(FieldKey.enroll.rawValue): \(fields[.enroll]?.value ?? "none")")
}
func binding(for key: FieldKey) -> Binding<FormFieldModel> {
return Binding(get: {
return self.fields[key]!
}, set: {
print("Model is being set with value \($0)")
self.fields[key] = $0
print("Fields is \(self.fields)")
print("-------------------------------------------------------")
})
}
}
struct FloatingTextField: View {
let model: Binding<FormFieldModel>
var text: Binding<String> {
Binding {
return self.model.wrappedValue.value as? String ?? ""
} set: {
print("Setting value to \($0)")
self.model.wrappedValue.value = $0
}
}
var body: some View {
ZStack(alignment: .leading) {
Text(model.wrappedValue.title)
.foregroundColor(Color(.placeholderText))
.offset(y: text.wrappedValue.isEmpty ? 0 : -25)
.scaleEffect(text.wrappedValue.isEmpty ? 1 : 0.8, anchor: .leading)
TextField("", text: text) // give TextField an empty placeholder
}
.padding(.top, 15)
.animation(.spring(response: 0.2, dampingFraction: 0.5))
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color.blue, lineWidth: 1.5)
)
}
}
struct CheckboxView: View {
let model: Binding<FormFieldModel>
var selected: Binding<Bool> {
Binding {
return self.model.wrappedValue.value as? Bool ?? false
} set: {
self.model.wrappedValue.value = $0
}
}
var body: some View {
HStack {
Button(selected.wrappedValue ? "selected" : "deselected") {
selected.wrappedValue.toggle()
}
Text("Enroll?")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let address:AddressViewModel = .init(first: "Matt", last: "Gannon")
ContentView(existingAddress: address)
}
}
在其中一个文本字段中键入时,程序会打印以下内容。如您所见,仅保留键入的第一个字母:
Setting value to B
Model is being set with value FormFieldModel(title: "First Name", type: FormUI.FormFieldType.plainText, required: true, value: Optional("B"))
fields has been modified
new value of first is B
Fields is [FormUI.ContentView.FieldKey.first: FormUI.FormFieldModel(title: "First Name", type: FormUI.FormFieldType.plainText, required: true, value: Optional("B")), FormUI.ContentView.FieldKey.enroll: FormUI.FormFieldModel(title: "Enroll", type: FormUI.FormFieldType.checkbox, required: true, value: nil), FormUI.ContentView.FieldKey.last: FormUI.FormFieldModel(title: "Last Name", type: FormUI.FormFieldType.plainText, required: true, value: nil)]
-------------------------------------------------------
Setting value to Bo
Model is being set with value FormFieldModel(title: "First Name", type: FormUI.FormFieldType.plainText, required: true, value: Optional("Bo"))
fields has been modified
new value of first is B
Fields is [FormUI.ContentView.FieldKey.first: FormUI.FormFieldModel(title: "First Name", type: FormUI.FormFieldType.plainText, required: true, value: Optional("B")), FormUI.ContentView.FieldKey.enroll: FormUI.FormFieldModel(title: "Enroll", type: FormUI.FormFieldType.checkbox, required: true, value: nil), FormUI.ContentView.FieldKey.last: FormUI.FormFieldModel(title: "Last Name", type: FormUI.FormFieldType.plainText, required: true, value: nil)]
-------------------------------------------------------
Setting value to Bob
Model is being set with value FormFieldModel(title: "First Name", type: FormUI.FormFieldType.plainText, required: true, value: Optional("Bob"))
fields has been modified
new value of first is B
Fields is [FormUI.ContentView.FieldKey.first: FormUI.FormFieldModel(title: "First Name", type: FormUI.FormFieldType.plainText, required: true, value: Optional("B")), FormUI.ContentView.FieldKey.enroll: FormUI.FormFieldModel(title: "Enroll", type: FormUI.FormFieldType.checkbox, required: true, value: nil), FormUI.ContentView.FieldKey.last: FormUI.FormFieldModel(title: "Last Name", type: FormUI.FormFieldType.plainText, required: true, value: nil)]
-------------------------------------------------------
任何提示表示赞赏。我确定我在这里误解了有关 @State 或 Binding 的一些内容,这导致了混乱。谢谢