【问题标题】:SwiftUI observedobject with dynamic fields具有动态字段的 SwiftUI 观察对象
【发布时间】:2021-05-11 22:13:30
【问题描述】:

我是 SwiftUI 的新手。我有一个对象,它具有我想绑定到 UI 中的字段的各种属性。但是,UI 是动态创建的。例如,要包含的字段以及可以自定义的顺序。

在更新对象时无法刷新 UI(例如,当用户输入邮政编码时,我们会自动填充城市和州字段)我创建了一个简化示例。

我可以通过在创建 TextField 时处理每个单独的字段来使其工作,但想看看是否可以更通用。

这是 UI 显示属性的示例对象

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
        }
    }
}

示例字段结构:

struct TestField: Identifiable {
    let id = UUID().uuidString
    var label: String
    var fieldType: String
}

如果我这样做并处理每个字段,它就会起作用:

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)
                }
            }
        }
    }
}

希望能够做这样的事情并基于fieldtype而不是单个字段创建文本字段(因为实际对象具有更多属性):

                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))

                }

但是当我这样做时,我得到了错误:

无法将 String 类型的值转换为预期的参数类型 Binding<String>

我也尝试过使用状态变量,例如

@State var stringValue = ""

然后在文本字段的 .onAppear 中,我将相应属性(obj.state 等)的值分配给stringValue 状态变量。我认为如果更新了observedObject,控件可能会刷新,但不知道在哪里更新stringValue 状态变量以使其正常工作。

希望我解释了我想要做得足够好。有没有人知道如何让它工作?

谢谢

【问题讨论】:

  • 正如错误所说,您需要一个绑定(@Published@State@Binding 属性)。计算的属性或函数值不能是绑定。通过绑定,SwiftUI 会观察属性并在属性更改时自动更新 UI。在您的第二个代码块中,没有什么可以告诉 SwiftUI 在zip 发生更改时更新视图。您可能需要更改您的 TestField 对象,以便它接受绑定,然后您可以在 TextField - TestField(label: "City", fieldType: "Text", fieldValue: $obj.zip) 中使用该绑定

标签: ios swift swiftui


【解决方案1】:

getStringValue(field: String) 函数的问题在于它返回一个 String 值(常量),而 SwiftUI TextField View 将一个 Binding 作为其 text 参数。

因此,SwiftUI 抛出该错误,因为它无法自动将 String 值转换为该字符串的 Binding。 解决此问题的一种方法是将 getStringValue(field: String) 函数移动到 ContentView 中,而不是返回 String,而应返回字符串的 Binding

所以,函数应该是这样的:

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("")
    }
}

此时,您应该不会再次出现该错误,并且代码应该可以正确编译。

当然,您仍然可以通过使用Structenum 而不是hard-coded 字符串值来改进这一点。

【讨论】:

  • 除上述之外,我还尝试演示如何使用 enum 和 struct 来实现相同的结果。查看此要点:SwiftUIdynamicfields
  • 非常感谢。这很有帮助。
猜你喜欢
  • 1970-01-01
  • 2015-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多