结构数组中的SWIFT结构未更新为新值

carvr3hs  于 2022-10-23  发布在  Swift
关注(0)|答案(2)|浏览(210)

这是我的数据结构

struct SPPWorkout: Codable {
    static let setKey = "Sets"
    static let exerciseID = "id"

    var id: Double? = 0.0
    var duration: String?
    var calories: Int?
    var date: String?
    var exercises: [ExerciseSet]

    [...]
}

struct ExerciseSet: Codable {
    let id: String
    let name: String
    var reps: Int
    var weight: Double

    [...]
}

extension ExerciseSet: Equatable {
    static func ==(lhs: ExerciseSet, rhs: ExerciseSet) -> Bool {
        return lhs.id == rhs.id
    }
}

在SwiftUI视图中,我尝试从用户输入修改ExerciseSet

@State private var sppWorkout: SPPWorkout!

                    EditSetPopup(isShowingOverlay: $isShowingOverlay,
                                 update: { reps, weight in
                        guard let editingIndex = editingIndex else { return }
                        sppWorkout.exercises[editingIndex].reps = Int(reps) ?? 0
                        sppWorkout.exercises[editingIndex].weight = Double(weight) ?? 0.0

                        self.editingIndex = nil
                    })
                }

问题就在这里

sppWorkout.exercises[editingIndex].reps = Int(reps) ?? 0
sppWorkout.exercises[editingIndex].weight = Double(weight) ??

我已经尝试了所有方法来更新它,无论是从视图还是使用SPPWorkout中的函数。我还尝试替换索引中的对象

var newSet = ExerciseSet(id: [...], newValues)
self.exercises[editingIndex] = newSet

但它绝对不想更新。我确信它会在某个地方创建一个副本,然后对其进行编辑,但我不知道为什么以及如何设置新值。

编辑:如果我试着删除一些东西,它是好的

sppWorkout.exercises.removeAll(where: { $0 == sppWorkout.exercises[index]})

**编辑2:**传递Guard语句,不更改数组中的值。

**编辑3:*在下面Jared的建议下,我已经将现有数组复制到新数组中,设置新值,然后尝试将新值分配给原始数组,但它仍然不会覆盖。

EditSetPopup(isShowingOverlay: $isShowingOverlay,
                                 update: { reps, weight in
                        print(sppWorkout.exercises)
                        guard let editingIndex = editingIndex else { return }

                        var copyOfTheArray = sppWorkout.exercises
                        copyOfTheArray[editingIndex].reps = Int(reps) ?? 0
                        copyOfTheArray[editingIndex].weight = Double(weight) ?? 0.0
                        //Copy of the array is updated correctly, it has the new values

                        sppWorkout.exercises = copyOfTheArray
                        //Original array doesn't get overwritten. It still has old values

                        self.editingIndex = nil

**编辑4:*通过将模型提取到视图模型中并更新那里的值,我成功地取得了进展。现在sppWorkout中的值得到了更新,但即使我调用了objectWillChange.send(),UI更新也不会触发。

完整代码:

class WorkoutDetailsViewModel: ObservableObject {
    var workoutID: String!
    @Published var sppWorkout: SPPWorkout!

    func setupData(with workoutID: String) {
        sppWorkout = FileIOManager.readWorkout(with: workoutID)
    }

    func update(_ index: Int, newReps: Int, newWeight: Double) {
        let oldOne = sppWorkout.exercises[index]
        let update = ExerciseSet(id: oldOne.id, name: oldOne.name, reps: newReps, weight: newWeight)
        sppWorkout.exercises[index] = update

        self.objectWillChange.send()
    }
}

struct WorkoutDetailsView: View {
    var workoutID: String!
    @StateObject private var viewModel = WorkoutDetailsViewModel()

    var workout: HKWorkout
    var dateFormatter: DateFormatter

    @State private var offset = 0
    @State private var isShowingOverlay = false

    @State private var editingIndex: Int?
    @EnvironmentObject var settingsManager: SettingsManager
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        if viewModel.sppWorkout != nil {
            VStack {
                ListWorkoutItem(workout: workout, dateFormatter: dateFormatter)
                    .padding([.leading, .trailing], 10.0)

                List(viewModel.sppWorkout.exercises, id: \.id) { exercise in
                    let index = viewModel.sppWorkout.exercises.firstIndex(of: exercise) ?? 0

                    DetailListSetItem(exerciseSet: viewModel.sppWorkout.exercises[index], set: index + 1)
                        .environmentObject(settingsManager)
                        .swipeActions {
                            Button(role: .destructive, action: {
                                viewModel.sppWorkout.exercises.removeAll(where: { $0 == viewModel.sppWorkout.exercises[index]})
                            } ) {
                                Label("Delete", systemImage: "trash")
                            }

                            Button(role: .none, action: {
                                isShowingOverlay = true
                                editingIndex = index
                            } ) {
                                Label("Edit", systemImage: "pencil")
                            }.tint(.blue)
                        }
                }
                .padding([.leading, .trailing], -30)
                //iOS 16 .scrollContentBackground(.hidden)
            }
            .overlay(alignment: .bottom, content: {
                editOverlay
                    .animation(.easeInOut (duration: 0.5), value: isShowingOverlay)
            })
            .navigationBarBackButtonHidden(true)
            .navigationBarItems(leading: Button(action : {
                do {
                    try FileIOManager.write(viewModel.sppWorkout, toDocumentNamed: "\(viewModel.sppWorkout.id ?? 0).json")
                } catch {
                    Debugger.log(error: error.localizedDescription)
                }
                dismiss()
            }){
                Image(systemName: "arrow.left")
            })
        } else {
            Text("No workout details found")
                .italic()
                .fontWeight(.bold)
                .font(.system(size: 35))
                .onAppear(perform: {
                    viewModel.setupData(with: workoutID)
                })
        }
    }

    @ViewBuilder private var editOverlay: some View {
        if isShowingOverlay {
            ZStack {
                Button {
                    isShowingOverlay = false
                } label: {
                    Color.clear
                }
                .edgesIgnoringSafeArea(.all)
                VStack{
                    Spacer()
                    EditSetPopup(isShowingOverlay: $isShowingOverlay,
                                 update: { reps, weight in
                        guard let editingIndex = editingIndex else { return }
                        print(viewModel.sppWorkout.exercises)
                        print("dupa aia:\n")
                        viewModel.update(editingIndex, newReps: Int(reps) ?? 0, newWeight:  Double(weight) ?? 0.0)

                        print(viewModel.sppWorkout.exercises)

                        self.editingIndex = nil
                    })
                        .overlay(
                            RoundedRectangle(cornerRadius: 10)
                                .stroke(Color("popupBackground"),
                                        lineWidth: 3)
                        )
                }
            }
        }
    }
}
k5ifujac

k5ifujac1#

所以我在Reddit上得到了一个很好的解释,说明是什么导致了这个问题。如果你正在阅读这篇文章,谢谢你U/Neddy-Seagoon
这个解释是
。我相信更新数组不会触发状态更新。对于数组,唯一会发生的事情是如果计数发生变化。因此,sppWorkout.exerces[index].reps=newRep不会导致触发。这不会更改viewModel.sppWorkout.exercises.index
所以我所要做的就是修改我的清单

List(viewModel.sppWorkout.exercises, id: \.id)

List(viewModel.sppWorkout.exercises, id: \.hashValue)

因为这会触发列表更新,因为当更新列表中条目的属性时,hashValue确实会改变。

bttbmeg0

bttbmeg02#

List(viewModel.sppWorkout.exercises, id: \.id) { exercise in
替换为
List(viewModel.sppWorkout.exercises, id: \.self) { exercise in

相关问题