视图更改不会更新 object/model

Change in View does not update object/model

我遇到一个问题,即视图中的更改不会更新模型中的基础对象。这里的想法是生成所有不同类型(字符串、日期、布尔值)的动态属性列表,在 GUI 中一切看起来都很好,但是当点击“保存”按钮时,我可以看到数据没有更新。我在这里错过了什么?

完整的工作演示项目如下:

//
//  ContentView.swift
//  AttributeDemo
//
//  Created by Max on 28.05.22.
//

import SwiftUI

public enum AttributeType: Codable, CaseIterable{
    case int
    case string
    case datetime
    case decimal
    case double
    case boolean
    
    var stringValue: String {
        switch self {
            case .int: return "Full numbers"
            case .string: return "Text"
        case .datetime: return "Date"
        case .decimal: return "Decimal"
        case .double: return "Double"
        case .boolean: return "Yes/No"
        }
    }
}

public class Attribute: Identifiable, Codable, ObservableObject {
    public var id: UUID = UUID()
    public var bkey: String = ""
    public var tmp_create: Date = Date()
    public var itemBkey: String = ""
    public var attrType: AttributeType = .string
    public var name: String = ""
    public var description: String = ""
    public var value_int: Int = 0
    public var value_string: String = ""
    public var value_datetime: Date = Date()
    public var value_decimal: Decimal = 0.0
    public var value_double: Double = 0.0
    public var value_boolean: Bool = false
    var userBkey: String = ""
    var userToken: String = ""
}

struct ContentView: View {
    @State private var attributes: [Attribute] = []
    @State private var showingAttributeTypes = false
    
    var body: some View {
        VStack{
            Button("Add attribute"){
                self.showingAttributeTypes.toggle()
            }
            .confirmationDialog("Select a color", isPresented: $showingAttributeTypes, titleVisibility: .visible) {
                            Button("Text") {
                                addAttribute(attributeType: .string)
                            }
                Button("Number") {
                    addAttribute(attributeType: .decimal)
                }
                Button("Date") {
                    addAttribute(attributeType: .datetime)
                }
                Button("Yes/No") {
                    addAttribute(attributeType: .boolean)
                }
                        }
            
            ForEach(self.attributes){value in
                AttributeView(attribute: value)
            }
            
            Button("Save"){
                self.attributes.forEach{value in
                    print(value.attrType.stringValue)
                    print(value.value_string)
                    print(value.value_datetime)
                    print(value.value_boolean)
                    print("--------------------------------")
                }
            }
        }
    }
    
    func addAttribute(attributeType: AttributeType){
        var attribute = Attribute()
        attribute.attrType = attributeType
        self.attributes.append(attribute)
    }
}

struct AttributeView: View {
    @ObservedObject var attribute: Attribute = Attribute()
    @State private var description: String = ""
    @State private var value_boolean: Bool = false
    @State private var value_string: String = ""
    @State private var value_decimal: Decimal = 0.0
    @State private var value_double: Double = 0.0
    @State private var value_datetime: Date = Date()
    
    var body: some View {
        HStack{
            FormField(fieldName: "Description", fieldValue: $description)
                .keyboardType(.default)
            Spacer()
            switch(attribute.attrType){
            case .boolean:
                Toggle(isOn: $value_boolean) {
                    Label("", image: "")
                }
            case .string:
                TextField("", text: $value_string)
                    .keyboardType(.default)
            case .datetime:
                DatePicker(selection: $value_datetime, displayedComponents: .date, label: { Text("") })
            case .decimal:
                TextField("", value: $value_decimal, format: .number)
                    .keyboardType(.decimalPad)
            case .double:
                TextField("", value: $value_double, format: .number)
                    .keyboardType(.decimalPad)
            default:
                EmptyView()
            }
        }
    }
}

struct FormField: View {
    var fieldName = ""
    @Binding var fieldValue: String
    
    var body: some View{
        TextField(fieldName, text: $fieldValue)
        
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

要在视图中进行更改以更新模型中的基础对象,您可以尝试一小段 re-structure 代码,其中您将 Attribute 设为 struct,使用ObservableObject 模型以保留您的 Attributes 数组,并像本示例代码中那样使用它们:

public struct Attribute: Identifiable, Codable {  // <-- here
    public var id: UUID = UUID()
    public var bkey: String = ""
    public var tmp_create: Date = Date()
    public var itemBkey: String = ""
    public var attrType: AttributeType = .string
    public var name: String = ""
    public var description: String = ""
    public var value_int: Int = 0
    public var value_string: String = ""
    public var value_datetime: Date = Date()
    public var value_decimal: Decimal = 0.0
    public var value_double: Double = 0.0
    public var value_boolean: Bool = false
    var userBkey: String = ""
    var userToken: String = ""
}

public class AttributeModel: ObservableObject {   // <-- here
    @Published var attributes: [Attribute] = []  // <-- here
}

struct ContentView: View {
    @StateObject var model = AttributeModel()  // <-- here
    @State private var showingAttributeTypes = false
    
    var body: some View {
        VStack{
            Button("Add attribute"){
                self.showingAttributeTypes.toggle()
            }
            .confirmationDialog("Select a color", isPresented: $showingAttributeTypes, titleVisibility: .visible) {
                Button("Text") {
                    addAttribute(attributeType: .string)
                }
                Button("Number") {
                    addAttribute(attributeType: .decimal)
                }
                Button("Date") {
                    addAttribute(attributeType: .datetime)
                }
                Button("Yes/No") {
                    addAttribute(attributeType: .boolean)
                }
            }
            
            ForEach($model.attributes){ $value in  // <-- here
                AttributeView(attribute: $value)
            }
            
            Button("Save"){
                model.attributes.forEach { value in
                    print("---> \(value)")  // <-- here
                    print("--------------------------------")
                }
            }
        }
    }
    
    func addAttribute(attributeType: AttributeType){
        var attribute = Attribute()
        attribute.attrType = attributeType
        model.attributes.append(attribute)
    }
}

struct AttributeView: View {
    @Binding var attribute: Attribute  // <-- here
    
    var body: some View {
        HStack{
            FormField(fieldName: "Description", fieldValue: $attribute.description)
                .keyboardType(.default)
            Spacer()
            switch(attribute.attrType){
            case .boolean:
                Toggle(isOn: $attribute.value_boolean) { // <-- here etc...
                    Label("", image: "")
                }
            case .string:
                TextField("", text: $attribute.value_string)
                    .keyboardType(.default)
            case .datetime:
                DatePicker(selection: $attribute.value_datetime, displayedComponents: .date, label: { Text("") })
            case .decimal:
                TextField("", value: $attribute.value_decimal, format: .number)
                    .keyboardType(.decimalPad)
            case .double:
                TextField("", value: $attribute.value_double, format: .number)
                    .keyboardType(.decimalPad)
            default:
                EmptyView()
            }
        }
    }
}