使用对象收集表单数据并在 Swift 中设置默认值

Use an object to collect form data and set default value in Swift

我是 Swift 开发的新手,我一辈子都弄不明白。我想做的就是使用一个对象来收集表单数据,将其保存到 Realm,然后通过 API.

将其发送到服务器

在我发现的每个示例中,人们都在为其表单中的每个元素创建一个新的 State 变量。这对于具有许多字段的表单来说似乎不切实际,因此我尝试只创建一个具有与我需要的表单字段匹配的属性的对象。如果我不尝试设置任何默认值,这将按我的预期工作。但是,当我尝试为 init() 中的表单设置一些默认值时,出现了我不知道如何解决的错误。这是部分代码:

将用于收集表单数据的对象:

class RecordObject: ObservableObject {
   var routeId: Int?
   var typeId: Int?
   var inDate: Date?
   var outDate: Date?
   var nextDate: Date?
   // .... more properties
}

我想做的是在视图中,在 init() 中设置一些默认值,需要一些逻辑来导出值:

在视图中:

struct AddLauncherView: View {
    var route: Route // this is passed from a previous view that lists all the routes
    @StateObject var record: RecordObject = RecordObject() // this is where I want to store all the form data
    
    init(){
        _record.routeId = self.route.id // I get the error: Referencing property 'routeId' requires wrapped value of type 'RecordObject'
        self.record.inDate = Date() // this gives the error: 'self' used before all stored properties are initialized
        //self.record.nextDate = here I need to do some date math to figure out the next date
    }

    var body: some View {
        Form{
            DatePicker("In Date", selection: $record.inDate, displayedComponents: .date)
            // .... more form elements
        }
    }
}

我知道我可以在 RecordObject class 中添加默认值,但是有些属性需要一些逻辑来分配默认值。

有人可以帮助 Swift 菜鸟并给我一些指导来完成这项工作吗?我真的需要为视图中的每个表单字段创建一个状态变量吗?

您使用的 ObservableObject 不正确。您的数据模型应该是一个结构,然后 class 发布结构类型的值,因此:

struct Record: Identifiable {
    // First, save yourself some grief later and make it conform to Identifiable
    let id = UUID()
    // The listed variables are probably non-optional in your data model.
    // If they are optional, mark them as optional, otherwise
    var routeId: Int
    var typeId: Int
    var inDate: Date
    // These are more likely optional
    var outDate: Date?
    var nextDate: Date?
    // .... more properties
}

class RecordObject: ObservableObject {
    // Make the source of truth @Published so things are updated in your view
    @Published var records: [Record] = []

    // OR use an init
    @Published var records: [Record]

    init(records: [Records] {
        self.records = records

        // or call some function that imports/creates the records...
    }
}

如果您确实使用了 class (ObservableObject),您会希望使用 @Published 对属性进行注释。但是,使用 struct 和包含您需要的所有各种属性的 @State 变量可能是更好的主意。这可能看起来像这样:

struct Record {
    var routeId: Int?
    var typeId: Int?
    var inDate: Date?
    var outDate: Date?
    var nextDate: Date?
}

struct AddLauncherView: View {
    var route: Route
    @State var record: Record
    
    init(route: Route) {
        self.route = route
        _record = State(initialValue: Record(routeId: route.id,
                                             typeId: nil,
                                             inDate: Date(),
                                             outDate: nil,
                                             nextDate: nil))
    }

    var body: some View {
        Form{
            DatePicker("In Date",
                       selection: Binding<Date>(get: {record.inDate ?? Date()}, 
            //custom binding only necessary if inDate is Date? -- if you make it just Date, you can bind directly to $record.inDate
                                                set: {record.inDate = [=10=]}),
                       displayedComponents: .date)
            // .... more form elements
        }
    }
}