更新: 添加观察对象后,我仍然得到索引超出范围错误。这是我使用的所有代码,从第一个视图到最后一个视图
父母:
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)
}
}
}
答案 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])")
}
}