SwiftUI跨视图传递ExerciseSets数组遇初始化匹配错误求助
SwiftUI视图嵌套数据传递问题与@Binding报错修复
我在SwiftUI开发中遇到视图间数据传递问题,视图层级是ExSetView → SetRowView → SetRowView2,需要在ExSetView中获取SetRowView2创建的ExerciseSets数组,用它控制按钮逻辑。但在ExSetView中把setsInWorkout声明为@Binding变量时,一直报错:
No exact matches in call to initializer - Found candidate with type '(Binding<*>) -> Binding<Optional<*>>'
原代码示例
ExSetView代码
struct ExSetView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss let date = Date() @State var selectedItems = Set<Exercise>() @State private var sets : Int = 1 @Binding var setsInWorkout = [ExerciseSet] var body: some View { NavigationView { VStack (alignment: .leading) { Text("Sets in workout: \($setsInWorkout.count)") HStack { Text(date, style: .date) Button("Done"){ // save workout using setsInWorkout } } ForEach(Array(selectedItems), id: \.self) { e in HStack{ Text(e.exercisename) } ForEach(0..<sets, id: \.self) { number in SetRowView(selectedItem: e) } } } } } }
SetRowView代码
import SwiftUI extension View { func hideKeyboard() { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } } struct SetRowView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss @State var selectedItem = Exercise() var body: some View { VStack{ Button(action: { self.sets += 1 }) { Text("Add Set") } ForEach(0..<sets, id: \.self) { number in SetRowView2(selectedItem: selectedItem) } }} }
SetRowView2代码
struct SetRowView2: View { @State private var weight = "" @State private var reps = "" @State private var sets : Int = 1 @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss @State var selectedItem = Exercise() @State var setsInWorkout = [ExerciseSet]() func addExerciseSet() { let newExerciseSet = ExerciseSet(context: viewContext) newExerciseSet.exercise = selectedItem newExerciseSet.dateCompleted = Date() newExerciseSet.id = UUID() newExerciseSet.reps = Int16(reps) ?? 1 newExerciseSet.weight = Int16(weight) ?? 1 setsInWorkout.append(newExerciseSet) try? viewContext.save() dismiss() } var body: some View { HStack{ ForEach(0..<sets, id: \.self) { number in Form { Section { TextField("Reps", text: $reps) .frame(width:20) .keyboardType(.decimalPad) .onTapGesture { self.hideKeyboard() } TextField("Weight", text: $weight) .frame(width:20) .keyboardType(.decimalPad) .onTapGesture { self.hideKeyboard() } } } } Button(action: { addExerciseSet() }) { Text("Done") } } } }
解决方案
1. 修正@Binding语法错误
原代码中@Binding var setsInWorkout = [ExerciseSet]是错误写法:@Binding变量不能直接赋值类型,且如果ExSetView是根视图,应该用@State持有数据源,再通过Binding传递给子视图:
// ExSetView中修改 @State private var setsInWorkout: [ExerciseSet] = []
2. 逐层传递Binding数据
要实现底层视图数据回传到顶层,需将setsInWorkout的Binding逐层向下传递:
修改SetRowView,添加Binding参数
struct SetRowView: View { @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss let selectedItem: Exercise // 用let接收父视图传递的对象,不要用@State初始化 @Binding var setsInWorkout: [ExerciseSet] // 添加Binding参数 @State private var sets: Int = 1 // 补上原代码缺失的sets变量 var body: some View { VStack{ Button(action: { self.sets += 1 }) { Text("Add Set") } ForEach(0..<sets, id: \.self) { number in // 传递Binding给SetRowView2 SetRowView2(selectedItem: selectedItem, setsInWorkout: $setsInWorkout) } } } }
修改SetRowView2,用@Binding接收数据
把原有的@State var setsInWorkout = [ExerciseSet]()改成@Binding,同时selectedItem用let接收:
struct SetRowView2: View { @State private var weight = "" @State private var reps = "" @State private var sets : Int = 1 @Environment(\.managedObjectContext) var viewContext @Environment(\.dismiss) var dismiss let selectedItem: Exercise // 用let接收父视图传递的对象 @Binding var setsInWorkout: [ExerciseSet] // 改成Binding类型 func addExerciseSet() { let newExerciseSet = ExerciseSet(context: viewContext) newExerciseSet.exercise = selectedItem newExerciseSet.dateCompleted = Date() newExerciseSet.id = UUID() newExerciseSet.reps = Int16(reps) ?? 1 newExerciseSet.weight = Int16(weight) ?? 1 // 直接添加到绑定的顶层数组中 setsInWorkout.append(newExerciseSet) try? viewContext.save() dismiss() } var body: some View { HStack{ ForEach(0..<sets, id: \.self) { number in Form { Section { TextField("Reps", text: $reps) .frame(width:20) .keyboardType(.decimalPad) .onTapGesture { self.hideKeyboard() } TextField("Weight", text: $weight) .frame(width:20) .keyboardType(.decimalPad) .onTapGesture { self.hideKeyboard() } } } } Button(action: { addExerciseSet() }) { Text("Done") } } } }
修改ExSetView中调用SetRowView的代码
传递setsInWorkout的Binding:
// ExSetView的ForEach中修改 ForEach(0..<sets, id: \.self) { number in SetRowView(selectedItem: e, setsInWorkout: $setsInWorkout) }
3. 修正其他细节问题
- ExSetView中显示数组数量时,直接用
setsInWorkout.count,不需要加$:
Text("Sets in workout: \(setsInWorkout.count)")
核心逻辑说明
- @Binding的作用是让多层视图共享同一数据源,子视图修改数据时,顶层视图能实时获取更新。
- 逐层传递Binding确保从SetRowView2到ExSetView共享同一个
setsInWorkout数组,添加新元素时顶层视图立即感知。 - 子视图接收父视图传递的静态对象(如selectedItem)时,用
let而非@State,避免不必要的状态持有。
内容的提问来源于stack exchange,提问作者Rachel
相关产品推荐
相关产品推荐

