在修改子视图依赖于绑定对象的数组时,我在避免索引超出范围错误时遇到了一些麻烦。
我有一个称为WorkoutList的父视图。WorkoutList具有ActiveWorkoutStore的EnvironmentObject。ActiveWorkoutStore是一个ObservableObject,它具有Workout对象的数组。我有一个从ActiveWorkoutStore检索的积极锻炼的列表。我正在使用ForEach循环来处理这些活动锻炼的索引,并将对象绑定传递到称为EditWorkout的子视图,作为NavigationLink的目标。EditWorkout具有完成锻炼的按钮,该按钮会将其从ActiveWorkoutStore的锻炼阵列中删除,并将其添加到WorkoutHistoryStore。我从ActiveWorkoutStore的activeWorkouts数组中删除该对象时遇到麻烦,立即导致索引超出范围错误。我怀疑这是因为活动视图依赖于我刚刚删除的绑定对象。一世’ ve尝试了几种排列方式,包括将锻炼传递给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 ) } }
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) } } }
import SwiftUI struct Workout: Hashable, Codable, Identifiable { var id = UUID() var date = Date() var exercises: [WorkoutExercise] = [] }
ForEach<Range> 是常量范围容器(请注意下面的构造函数说明),构造后不允许对其进行修改。
ForEach<Range>
> extension ForEach where Data == Range<Int>, ID == Int, Content : View { > > /// Creates an instance that computes views on demand over a > *constant* > /// range. > /// > /// This instance only reads the initial value of `data` and so it > does not > /// need to identify views across updates. > /// > /// To compute views on demand over a dynamic range use > /// `ForEach(_:id:content:)`. > public init(_ data: Range<Int>, @ViewBuilder content: @escaping > (Int) -> Content) > }
如果要修改容器,则必须使用 ForEach(activeWorkout.exercises)
ForEach(activeWorkout.exercises)