在修改子视图依赖于绑定对象的数组时,我在避免索引超出范围错误时遇到了一些麻烦。
我有一个名为 WorkoutList 的父视图。 WorkoutList 有一个 ActiveWorkoutStore 的 EnvironmentObject。 ActiveWorkoutStore 是一个具有 Workout 对象数组的 ObservableObject。我有一个从 ActiveWorkoutStore 检索到的活跃锻炼列表。我使用 ForEach 循环来处理这些活动锻炼的索引,并将绑定到名为 EditWorkout 的子视图的对象传递为 NavigationLink 的目的地。 EditWorkout 有一个完成锻炼的按钮,该按钮将其从 ActiveWorkoutStore 的锻炼数组中删除并将其添加到 WorkoutHistoryStore 中。当我从 ActiveWorkoutStore 的 activeWorkouts 数组中删除此对象时,我遇到了麻烦,立即导致索引超出范围错误。我怀疑这是因为活动视图依赖于我刚刚删除的绑定对象。我尝试了几种排列方式,包括将锻炼传递给 EditWorkout,然后使用其 id 引用 ActiveWorkoutStore 中的锻炼来执行我的操作,但遇到了类似的麻烦。我在网上看到了很多遵循这种利用 ForEach 迭代索引的模式的示例,并且我已经尽可能地反映了它,但我怀疑我可能错过了该方法的细微差别。
我在下面附上了代码示例。如果您有任何问题或者我还需要添加其他内容,请告诉我!在此先感谢您的帮助!
锻炼列表(家长视图)
import SwiftUI
struct WorkoutList: View {
@EnvironmentObject var activeWorkoutsStore: ActiveWorkoutStore
@State private var addExercise = false
@State private var workoutInProgress = false
var newWorkoutButton: some View {
Button(action: {
self.activeWorkoutsStore.newActiveWorkout()
}) {
Text("New Workout")
Image(systemName: "plus.circle")
}
}
var body: some View {
NavigationView {
Group {
if activeWorkoutsStore.activeWorkouts.isEmpty {
Text("No active workouts")
} else {
List {
ForEach(activeWorkoutsStore.activeWorkouts.indices.reversed(), id: \.self) { activeWorkoutIndex in
NavigationLink(destination: EditWorkout(activeWorkout: self.$activeWorkoutsStore.activeWorkouts[activeWorkoutIndex])) {
Text(self.activeWorkoutsStore.activeWorkouts[activeWorkoutIndex].id.uuidString)
}
}
}
}
}
.navigationBarTitle(Text("Active Workouts"))
.navigationBarItems(trailing: newWorkoutButton)
}
}
}
编辑锻炼(子视图)
//
// EditWorkout.swift
// workout-planner
//
// Created by Dominic Minischetti III on 11/2/19.
// Copyright © 2019 Dominic Minischetti. All rights reserved.
//
import SwiftUI
struct EditWorkout: View {
@EnvironmentObject var workoutHistoryStore: WorkoutHistoryStore
@EnvironmentObject var activeWorkoutStore: ActiveWorkoutStore
@EnvironmentObject var exerciseStore: ExerciseStore
@Environment(\.presentationMode) var presentationMode
@State private var addExercise = false
@Binding var activeWorkout: Workout
var currentDayOfWeek: String {
let weekdayIndex = Calendar.current.component(.weekday, from: Date())
return Calendar.current.weekdaySymbols[weekdayIndex - 1]
}
var chooseExercisesButton: some View {
Button (action: {
self.addExercise = true
}) {
HStack {
Image(systemName: "plus.square")
Text("Choose Exercises")
}
}
.sheet(isPresented: self.$addExercise) {
AddWorkoutExercise(exercises: self.$activeWorkout.exercises)
.environmentObject(self.exerciseStore)
}
}
var saveButton: some View {
Button(action: {
self.workoutHistoryStore.addWorkout(workout: self.$activeWorkout.wrappedValue)
self.activeWorkoutStore.removeActiveWorkout(workout: self.$activeWorkout.wrappedValue)
self.presentationMode.wrappedValue.dismiss()
}) {
Text("Finish Workout")
}
.disabled(self.$activeWorkout.wrappedValue.exercises.isEmpty)
}
var body: some View {
Form {
Section(footer: Text("Choose which exercises are part of this workout")) {
chooseExercisesButton
}
Section(header: Text("Exercises")) {
if $activeWorkout.wrappedValue.exercises.isEmpty {
Text("No exercises")
} else {
ForEach(activeWorkout.exercises.indices, id: \.self) { exerciseIndex in
NavigationLink(destination: EditWorkoutExercise(exercise: self.$activeWorkout.exercises[exerciseIndex])) {
VStack(alignment: .leading) {
Text(self.activeWorkout.exercises[exerciseIndex].name)
Text("\(self.activeWorkout.exercises[exerciseIndex].sets.count) Set\(self.activeWorkout.exercises[exerciseIndex].sets.count == 1 ? "" : "s")")
.font(.footnote)
.opacity(0.5)
}
}
}
saveButton
}
}
}
.navigationBarTitle(Text("Edit Workout"), displayMode: .inline )
}
}
ActiveWorkout商店
import Foundation
import Combine
class ActiveWorkoutStore: ObservableObject {
@Published var activeWorkouts: [Workout] = []
func newActiveWorkout() {
activeWorkouts.append(Workout())
}
func saveActiveWorkout(workout: Workout) {
let workoutIndex = activeWorkouts.firstIndex(where: { $0.id == workout.id })!
activeWorkouts[workoutIndex] = workout
}
func removeActiveWorkout(workout: Workout) {
if let workoutIndex = activeWorkouts.firstIndex(where: { $0.id == workout.id }) {
activeWorkouts.remove(at: workoutIndex)
}
}
}
Workout
import SwiftUI
struct Workout: Hashable, Codable, Identifiable {
var id = UUID()
var date = Date()
var exercises: [WorkoutExercise] = []
}