按钮操作块内的 Foreach 循环抛出“Type() 不能符合视图”

Posted

技术标签:

【中文标题】按钮操作块内的 Foreach 循环抛出“Type() 不能符合视图”【英文标题】:Foreach loop inside button action block throws "Type() cannot conform to view" 【发布时间】:2020-07-31 22:19:34 【问题描述】:

我有一组视图,我需要从中获取值以便将它们保存到我的上下文中。当单击按钮时尝试遍历这些时,我得到“类型'()'不能符合'视图';只有结构/枚举/类类型可以符合协议”异常。我对 swift 很陌生,我想知道是否有更简单的方法可以做到这一点。提前谢谢你!

这是完整的视图:

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 public var showPassedWorkouts : Bool = false
    
    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: 
                        let workout = Workout(context: self.moc)
                        workout.muscle = self.targetMuscle
                        workout.date = formattedDateString(day: self.today)
                        
                        ForEach(0..<exerciseCards.count, id: \.self)number in
                            let exercise = Exercise(context: self.moc)
                            ForEach(0..<self.exerciseCards[number].tableRows.count, id: \.self)innerNum in
                                let exerciseSet = ExerciseSet(context: self.moc)
                                exerciseSet.reps = self.exerciseCards[number].tableRows[innerNum].reps
                                exerciseSet.weight = self.exerciseCards[number].tableRows[innerNum].weight
                                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: 
                        self.exerciseCards.append(ExerciseCard())
                    ) 
                        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)
            
    
    


func formattedDateString(format: String? = "MMM d, h:mm", day: Date) -> String 
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = format
    return dateFormatter.string(from: day)

上面的视图还引用了两个视图,第一个是练习卡视图

struct ExerciseCard: View 
    @State public var exercise : String = "Bench Press"
    @State public var tableRows : [ExerciseTableRow] = []
    
    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: 
                    self.tableRows.append(ExerciseTableRow(set: 2, readOnly: false, setWeight: 2, setReps: 2))
                ) 
                    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)
    

第二个是 ExerciseTableRow 视图

struct ExerciseTableRow: View 
    @State public var weight : String = "0"
    @State public var reps : String = "0"
    var set : Int16
    var readOnly : Bool
    var setWeight : Int16
    var setReps : Int16
    var body: some View 
        
        HStack
            Text(String(set))
                .padding(.trailing, 40)
                .padding(.leading, 10)
            if readOnly == false
                Group
                    TextField("0", text: $weight)
                    TextField("0", text: $reps)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                    .frame(width: 50)
                    .multilineTextAlignment(.center)
                    .keyboardType(.numberPad)
                    .padding(.horizontal, 30)
            
            else
                Group
                    Text(String(setWeight))
                    Text(String(setReps))
                
                .frame(width: 50)
                    .overlay(
                        RoundedRectangle(cornerRadius: 5)
                            .stroke(Color.black, lineWidth: 1)
                    )
                    .padding(.bottom, 5)
                    .padding(.horizontal, 30)
            
            
        
        
        
    

【问题讨论】:

请添加更多上下文。至少发布视图代码而不仅仅是一个按钮。 @pawello2222 抱歉延迟回复,但我更新了上面的代码 【参考方案1】:

ForEach 是一个 SwiftUI 动态视图容器,用于循环构建视图。对于操作,您必须使用常规 swift for .. in 表达式,例如

Button(action: 
    let workout = Workout(context: self.moc)
    workout.muscle = self.targetMuscle
    workout.date = formattedDateString(day: self.today)

    for number in 0..<exerciseCards.count 
        let exercise = Exercise(context: self.moc)
        for innerNum in 0..<self.exerciseCards[number].tableRows.count 
            let exerciseSet = ExerciseSet(context: self.moc)
            exerciseSet.reps = self.exerciseCards[number].tableRows[innerNum].reps
            exerciseSet.weight = self.exerciseCards[number].tableRows[innerNum].weight
            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)

【讨论】:

非常感谢!如果你不介意,还有一件事。在那个 for 循环中,我使用我的数组来访问不同视图中的变量。这些变量连接到文本字段。当我将它们保存到我的上下文中时,我的文本字段不会更新这些变量。从我分享的所有代码中,你有没有看到任何作为这背后的问题突然出现在你面前的东西?例如,如果我尝试访问“self.exerciseCards[number].tableRows[innerNum].reps”行上的 reps 变量,它总是返回 0。如果这与它有任何关系,该变量就是一个 @State 变量。

以上是关于按钮操作块内的 Foreach 循环抛出“Type() 不能符合视图”的主要内容,如果未能解决你的问题,请参考以下文章

剃刀块内的asp.net mvc 4 javascript抛出错误

队列ForEach循环抛出InvalidOperationException

滑块内的按钮不会从画外音 (a11y) 中调出

使用swift更改动画块内的UIbutton

R语言使用Repeat函数多次执行代码块内的语句,实现循环执行任务的功能:repeat没有提供任何检查条件,所以编码者必须给出退出重复循环的条件(一般使用if和break)

在 foreach 循环中抛出 IndexOutOfRangeException