访问数组SwiftUI时使索引超出范围异常

时间:2020-08-01 22:14:31

标签: swift swiftui

更新: 添加观察对象后,我仍然得到索引超出范围错误。这是我使用的所有代码,从第一个视图到最后一个视图

父母:

struct DetailedView: View {
    
    @Environment(\.managedObjectContext) var moc
    
    var targetMuscle : String = "Chest"
    let today = Date()
    @State public var exerciseCards : [ExerciseCard] = []
    @State public var exercise : String = "Bench Press"
    @State public var exercises : Int = 0
    @State private var showPassedWorkouts : Bool = false
    @State private var savedWorkout : Bool = false
    @State private var exerciseNames : [String] = []
    @State private var exerciseRows : [[ExerciseTableRow]] = [[]]
    
    //@State private var exerciseSets : [[Int16]] = [[]]
    //@State private var setWeight : [[Int16]] = [[]]
    //@State private var setReps : [[Int16]] = [[]]
    
    @ObservedObject var viewModel = ViewModel()
    @State var exerciseIndex : Int = 0
    
    static let taskDateFormat : DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .long
        return formatter
    }()
    
    var body: some View {
        ZStack{
            VStack{
                HStack{
                    VStack{
                        Text(targetMuscle).font(.system(size:40)).fontWeight(.medium)
                        Text("\(today, formatter: Self.taskDateFormat)")
                            .font(.system(size:20))
                    }.frame(width: 250, height: 30, alignment: .topLeading)
                        .navigationBarTitle("")
                        .navigationBarHidden(true)
                        .padding(.bottom, -7)
                    Button(action: {
                        self.showPassedWorkouts.toggle()
                    }) {
                        Text("Passed Workouts")
                            .multilineTextAlignment(.center)
                        
                        
                    }.offset(x: -75, y: 25)
                        .sheet(isPresented: $showPassedWorkouts){
                            PassedWorkoutList()
                    }
                    
                    
                    Button(action: {
                        
                        for number in 0..<self.exerciseCards.count{
                            print(self.exerciseCards[number].exercise)
                            /*for innernum in 0..<self.exerciseCards[number].tableRows.count{
                                print(self.exerciseCards[number].tableRows[innernum].setReps)
                                print(self.exerciseCards[number].tableRows[innernum].setWeight)
                                print(self.exerciseCards[number].tableRows[innernum].set)
                            }*/
                        }
                        
                        /*let workout = Workout(context: self.moc)
                        workout.muscle = self.targetMuscle
                        workout.date = formattedDateString(day: self.today)
                        
                        for number in 0..<self.exerciseCards.count{
                            let exercise = Exercise(context: self.moc)
                            exercise.name = self.exerciseCards[number].exercise
                            for innerNum in 0..<self.exerciseCards[number].tableRows.count{
                                let exerciseSet = ExerciseSet(context: self.moc)
                                exerciseSet.reps = Int16(self.exerciseCards[number].tableRows[innerNum].reps) ?? 0
                                exerciseSet.weight = Int16(self.exerciseCards[number].tableRows[innerNum].weight) ?? 0
                                exerciseSet.set = self.exerciseCards[number].tableRows[innerNum].set
                                exercise.addToExerciseSet(exerciseSet)
                            }
                            workout.addToExercise(exercise)
                        }
                        
                        try? self.moc.save()*/
                        
                        
                    }) {
                        Text("Finish")
                    }.offset(x: -20, y: 20)
                }.padding(.bottom, 35)
                    .padding(.leading)
                
                
                
                ScrollView{
                    ForEach(0..<exerciseCards.count, id: \.self){ number in
                        self.exerciseCards[number]
                    }
                    
                    
                    Button(action: {
                        
                        //let tempExerciseRow : [ExerciseTableRow] = []
                        //let tempExerciseSets : [Int16] = []
                        let tempSetReps : [Int16] = []
                        let tempSetWeight : [Int16] = []
                        
                        self.viewModel.setReps.append([Int16(12), Int16(12)])
                        self.viewModel.setWeight.append([Int16(12), Int16(12)])
                        
                        
                        print(self.viewModel.setReps)
                        
                        
                        
                        print(self.viewModel.setReps)
                        print(self.viewModel.setWeight)
                        
                        self.exerciseNames.append("")
                        //self.exerciseRows.append(tempExerciseRow)
                        //self.exerciseSets.append(tempExerciseSets)
                        /*self.setWeight.append(tempSetWeight)
                        self.setReps.append(tempSetReps)
                        
                        self.exerciseCards.append(ExerciseCard(exercise: self.$exerciseNames[self.exerciseNames.count - 1], tableRows: self.$exerciseRows[self.exerciseNames.count - 1], tableSet: self.$exerciseSets[self.exerciseNames.count - 1], setReps: self.$setReps[self.exerciseNames.count - 1], setWeight: self.$setWeight[self.exerciseNames.count - 1]))*/
                        
                        
                        self.exerciseCards.append(ExerciseCard(exercise: self.$exerciseNames[self.exerciseNames.count - 1],viewModel: self.viewModel,index : self.exerciseIndex))
                        
                        self.exerciseIndex += 1
                        
                        //self.exerciseCards.append(ExerciseCard(exercise: self.$exerciseNames[self.exerciseNames.count - 1], tableRows:self.$exerciseRows[self.exerciseNames.count - 1]))
                        
                        
                    }) {
                        Text("Add Exercise")
                            .frame(minWidth: 325)
                            .padding()
                            .foregroundColor(.white)
                            .background(Color.blue.opacity(0.7))
                            .cornerRadius(20)
                        
                    }.padding(.top)
                        .frame(width: 400)
                }
                
                
            }
        }.background(Color.white)
            
    }
    
}

第一个子视图

struct ExerciseCard: View {
    @Binding public var exercise : String
    @State public var tableRows : [ExerciseTableRow] = []
    
    @ObservedObject var viewModel : ViewModel
    var index : Int = 0
    
    //@Binding public var tableSet : [Int16]
    //@Binding public var setReps : [Int16]
    //@Binding public var setWeight : [Int16]
    
    @State var numberOfRow : Int = 0
    
    
    var body: some View {
        VStack{
            TextField("Enter Exercise", text: $exercise).textFieldStyle(RoundedBorderTextFieldStyle())
            .frame(width: 300)
            .multilineTextAlignment(.center)
            HStack{
                Group{
                    Text("Set")
                    Text("Weight")
                    Text("Reps")
                }.padding(.horizontal, 30)
                .offset(x: -20, y: 0)
                
            }
            VStack{
                
                ForEach(0..<tableRows.count, id: \.self){ number in
                    self.tableRows[number]
                    
                    
                }
            }.padding(.bottom, 5)
            
            HStack{
                Button(action: {
                    if self.tableRows.count > 1{
                        self.tableRows.remove(at: self.tableRows.count-1)
                    }
                        
                }) {
                    Text("Remove Set")
                        .frame(minWidth: 150)
                        .padding(.vertical, 5)
                        .foregroundColor(.white)
                        .background(Color.red)
                        .cornerRadius(20)
                    
                }
                
                Button(action: {
                    
                    /*print(self.tableSet.isEmpty)
                    print(self.setReps.isEmpty)
                    print(self.setWeight.isEmpty)
                    
                    
                    
                    self.tableSet.append(Int16(self.numberOfRows + 1))
                    self.setReps.append(Int16(0))
                    self.setWeight.append(Int16(0))
                    
                    print(self.tableSet[self.numberOfRows])
                    print(self.setReps[self.numberOfRows])
                    print(self.setWeight[self.numberOfRows])
                    
                    self.tableRows.append(ExerciseTableRow(set: self.$tableSet[self.tableSet.count - 1], setWeight: self.$setWeight[self.setWeight.count - 1], setReps: self.$setReps[self.setReps.count - 1]))*/
                    
                    print(self.index)
                    print(self.numberOfRow)
                    
                    
                    //self.viewModel.setReps[self.index][self.numberOfRow] = Int16(0)
                    //self.$viewModel.setWeight[self.index][self.numberOfRow]
                    
                    self.tableRows.append(ExerciseTableRow(exerciseIndex: self.index, rowIndex: self.numberOfRow, viewModel: self.viewModel, set: self.numberOfRow + 1))
                    
                    self.numberOfRow += 1
                    
                }) {
                    Text("Add Set")
                        .frame(minWidth: 150)
                        .padding(.vertical, 5)
                        .foregroundColor(.white)
                        .background(Color.green)
                        .cornerRadius(20)
                    
                }
                
            }
        }
        .padding()
        .padding(.vertical)
        .background(Color.offWhite)
        .cornerRadius(20)
        .shadow(color: Color.black.opacity(0.2), radius: 10, x:10, y:10)
        .shadow(color: Color.white.opacity(0.7), radius: 10, x:-5, y:-5)
    }
}

第二个子视图(这是我将数组超出范围的错误。当我尝试将2d数组与文本字段一起使用时

struct ExerciseTableRow: View {
    //@State public var weight : String = "0"
    //@State public var reps : String = "0"
    //@Binding var set : Int16
    //@Binding var setWeight : Int16
    //@Binding var setReps : Int16
    
    var exerciseIndex : Int
    var rowIndex : Int
    @ObservedObject var viewModel : ViewModel
    var set : Int
    
    var body: some View {
        
        HStack{
            Text(String(set))
                .padding(.trailing, 40)
                .padding(.leading, 10)
            
            Group{
                TextField("0", value: self.$viewModel.setWeight[exerciseIndex][rowIndex], formatter: NumberFormatter())
                TextField("0", value: self.$viewModel.setReps[exerciseIndex][rowIndex], formatter: NumberFormatter())
            }.textFieldStyle(RoundedBorderTextFieldStyle())
                .frame(width: 50)
                .multilineTextAlignment(.center)
                .keyboardType(.numberPad)
                .padding(.horizontal, 30)
            
            
        }
        
        
    }
}

1 个答案:

答案 0 :(得分:1)

@Binding属性适用于两级层次结构。但是,如果您需要将它们传递给更多的嵌套视图,则可以改用@ObservedObject(甚至是@EnvironmentObject)。

在您的示例中,尝试使用专用的ObservableObject类:

class ViewModel: ObservableObject {
    @Published public var exercise: String
}

struct ExerciseCard: View {
    @ObservedObject var viewModel: ViewModel // can be passed from the parent view (remember to only declare it once)
    ...
}

这意味着不做:

ExerciseTableRow(set: self.$tableSet[self.numberOfRows], setWeight: self.$setWeight[self.numberOfRows], setReps: self.$setReps[self.numberOfRows])

您可以将ObservableObject和索引传递到子视图:

struct ExerciseTableRow: View {
    @ObservedObject var viewModel: ViewModel
    let index: Int
    ...
}

并直接从@ObservedObject访问绑定:

self.$viewModel.exercise

处理索引是有风险的,请始终尝试确保您确实可以访问该元素。使用SwiftUI并不确定,请查看Deleting list elements from SwiftUI's List以了解它的不可预测性。

通常,在访问数组中的元素之前,应检查它是否存在:

if index < array.count - 1 {
    // now we are sure we can access `array[index]`
}

如果要在SwiftUI视图中执行此操作,可以使用@ViewBuilder

@ViewBuilder
var body: some View {
    if index < array.count - 1 {
        Text("\(array[index])")
    }
}