在 SwiftUI 中 Update/Edit 数组元素的最佳方式

Best Way to Update/Edit an Array Element in SwiftUI

我有一系列可识别的培训元素。每个 Training 元素只有两个属性,name 和 isRequired。

将数组元素的现有值更新为新的编辑值的最直接(最快捷)的方法是什么...编辑后的值稍后将提交到数据库。

是否可以将 EditTraining 视图(子)中的状态设置为传入的(父)值,然后在子视图中编辑状态?

我承认这一点的时间比我承认的还要长。

非常感谢您的帮助!

代码如下:

import SwiftUI

struct Training: Identifiable {
    let id: String
    let trainingName: String
    let isRequired: Bool
}

class GetTrainings: ObservableObject {
    @Published var items = [Training]()

    init() {
        self.items = [
            Training(id: "ttt1", trainingName: "Safety", isRequired: true),
            Training(id: "ttt2", trainingName: "Administrative", isRequired: false),
            Training(id: "ttt3", trainingName: "Computer", isRequired: true),
            Training(id: "ttt4", trainingName: "People", isRequired: true),
            Training(id: "ttt5", trainingName: "Managerial", isRequired: true),
        ]
    }
}

struct TrainingList: View {

    @ObservedObject var trainings = GetTrainings()

    var body: some View {
        NavigationView {
            VStack {
                List {

                    ForEach(trainings.items) { training in

                        HStack {
                            NavigationLink(destination: TrainingView(training: training)) {
                                Text("\(training.trainingName)")
                            }
                        }
                    }

                }
            }.navigationBarTitle("Training List")
        }
    }
}

struct TrainingView: View {

    var training: Training

    var body: some View {

        VStack {

            Text("\(training.trainingName)").font(.body)
            Text("\(training.isRequired == true ? "Required Training" : "Training Not Required")")

            HStack {
                NavigationLink(destination: EditTraining(training: training)) {
                    Text("Edit Training Details")
                }
            }
        }.navigationBarTitle("\(training.trainingName) Page", displayMode: .inline)

    }
}

struct EditTraining: View {

    var training: Training

    // Can I set the state values to the passed in values ???
    @State private var newName: String = ""
    @State private var isRequiredTraining: Bool = false
    //@Binding var name: String = training.trainingName ????

    private func submitData() {

        let newName = self.newName
        let newBoolVal = self.isRequiredTraining

        print("Firebase Sync Id is :\(training.id) Text: \(newName) and Bool: \(newBoolVal)")

    }

    var body: some View {
        VStack {
            Form {
                Section (header: Text("Edit"))  {

                    Text("\(training.trainingName)")
                    /* TextField should Populate With passed In Training Name Here*/
                    TextField("New Name", text: self.$newName)
                    Toggle(isOn: self.$isRequiredTraining) {
                        Text("Is Required")
                    }
                }

                Section {

                    Button(action: {
                        self.submitData()
                    }) {
                        Text("Submit")
                    }

                }
            }
        }.navigationBarTitle("Edit Training Page", displayMode: .inline)
    }
}

struct ContentView: View {

    var body: some View {

        VStack {

            TrainingList()

        }
    }
}

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

在这种情况下,我认为对模型使用 ObservableObject 更可取,因为它允许将参考模型对象深入层次结构并在工作流程中保持最新。

这是一个解决方案。使用 Xcode 11.4 / iOS 13.4

测试

class Training: ObservableObject, Identifiable {
    let id: String
    @Published var trainingName: String
    @Published var isRequired: Bool

    init(id: String, trainingName: String, isRequired: Bool) {
        self.id = id
        self.trainingName = trainingName
        self.isRequired = isRequired
    }
}

class GetTrainings: ObservableObject {
    @Published var items = [Training]()

    init() {
        self.items = [
            Training(id: "ttt1", trainingName: "Safety", isRequired: true),
            Training(id: "ttt2", trainingName: "Administrative", isRequired: false),
            Training(id: "ttt3", trainingName: "Computer", isRequired: true),
            Training(id: "ttt4", trainingName: "People", isRequired: true),
            Training(id: "ttt5", trainingName: "Managerial", isRequired: true),
        ]
    }
}

struct TrainingList: View {

    @ObservedObject var trainings = GetTrainings()

    var body: some View {
        NavigationView {
            VStack {
                List {

                    ForEach(trainings.items) { training in

                        HStack {
                            NavigationLink(destination: TrainingView(training: training)) {
                                Text("\(training.trainingName)")
                            }
                        }
                    }

                }
            }.navigationBarTitle("Training List")
            .onAppear {
                self.trainings.objectWillChange.send() // refresh
            }
        }
    }
}

struct TrainingView: View {

    @ObservedObject var training: Training

    var body: some View {

        VStack {

            Text("\(training.trainingName)").font(.body)
            Text("\(training.isRequired == true ? "Required Training" : "Training Not Required")")

            HStack {
                NavigationLink(destination: EditTraining(training: training)) {
                    Text("Edit Training Details")
                }
            }
        }.navigationBarTitle("\(training.trainingName) Page", displayMode: .inline)

    }
}

struct EditTraining: View {

    @ObservedObject var training: Training

    @State private var newName: String
    @State private var isRequiredTraining: Bool

    init(training: Training) {
        self.training = training
        self._newName = State(initialValue: training.trainingName)
        self._isRequiredTraining = State(initialValue: training.isRequired)
    }

    private func submitData() {

        let newName = self.newName
        let newBoolVal = self.isRequiredTraining

        print("Firebase Sync Id is :\(training.id) Text: \(newName) and Bool: \(newBoolVal)")

        self.training.trainingName = newName
        self.training.isRequired = newBoolVal
    }

    var body: some View {
        VStack {
            Form {
                Section (header: Text("Edit"))  {

                    Text("\(training.trainingName)")
                    /* TextField should Populate With passed In Training Name Here*/
                    TextField("New Name", text: self.$newName)
                    Toggle(isOn: self.$isRequiredTraining) {
                        Text("Is Required")
                    }
                }

                Section {

                    Button(action: {
                        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder),
                            to:nil, from:nil, for:nil)
                        self.submitData()
                    }) {
                        Text("Submit")
                    }

                }
            }
        }.navigationBarTitle("Edit Training Page", displayMode: .inline)
    }
}

backup