SwiftUI嵌套ForEach循环中如何为每个迭代设置独立变量
SwiftUI嵌套ForEach实现独立数据修改的解决方案
问题根源
你当前用单个@State private var reps: Int变量存储次数,所有Stepper共享同一个状态,所以修改任意一个Stepper都会同步改变所有显示的数值。
方案一:修改模型存储每组数据(推荐)
这种方式符合SwiftUI的MVVM设计思想,把每组的次数数据直接存在练习模型里,数据和视图分离,更易维护。
1. 调整Exercise模型
给Exercise添加一个数组属性,专门存储每组的次数,同时确保Exercise遵循Identifiable(方便ForEach使用):
import SwiftUI class Workout: ObservableObject { @Published var exercises: [Exercise] = [] } struct Exercise: Identifiable, Hashable { let id = UUID() var nameOfExercise: String var amountOfSets: Int // 新增数组,每个元素对应一组的次数,初始化时设为全0 @Published var repsPerSet: [Int] }
2. 初始化练习数据
创建练习时,确保repsPerSet的长度和amountOfSets一致:
// 示例:创建一个3组的俯卧撑练习 let pushUpExercise = Exercise( nameOfExercise: "俯卧撑", amountOfSets: 3, repsPerSet: Array(repeating: 0, count: 3) )
3. 修改视图代码
使用绑定语法$workout.exercises让子视图可以修改练习的属性,直接绑定每组对应的数组元素:
struct TrainingView: View { // Swift规范:结构体首字母大写 @ObservedObject var workout: Workout var body: some View { ForEach($workout.exercises) { $exercise in Text(exercise.nameOfExercise) .font(.headline) ForEach(0..<exercise.amountOfSets, id: \.self) { setIndex in VStack(alignment: .leading) { Text("\(setIndex + 1) Set") HStack { Text("次数:\(exercise.repsPerSet[setIndex])") Stepper("调整", value: $exercise.repsPerSet[setIndex], in: 0...30) } } .padding(.vertical, 4) } .padding(.bottom, 12) } } }
方案二:用字典临时存储状态(无需修改原模型)
如果不想改动原有的Exercise模型,可以在视图里用字典来临时存储每组的次数:
struct TrainingView: View { @ObservedObject var workout: Workout // 字典键:练习ID;字典值:对应每组的次数数组 @State private var repsDict: [UUID: [Int]] = [:] var body: some View { ForEach(workout.exercises) { exercise in Text(exercise.nameOfExercise) .font(.headline) ForEach(0..<exercise.amountOfSets, id: \.self) { setIndex in // 提前初始化当前练习的次数数组,避免nil let _ = repsDict[exercise.id, default: Array(repeating: 0, count: exercise.amountOfSets)] VStack(alignment: .leading) { Text("\(setIndex + 1) Set") HStack { Text("次数:\(repsDict[exercise.id]?[setIndex] ?? 0)") Stepper("调整", value: Binding( get: { repsDict[exercise.id]?[setIndex] ?? 0 }, set: { newValue in guard var sets = repsDict[exercise.id] else { return } sets[setIndex] = newValue repsDict[exercise.id] = sets } ), in: 0...30) } } .padding(.vertical, 4) } .padding(.bottom, 12) } } }
方案对比
- 方案一更适合长期维护,数据存在模型中,视图销毁后数据不会丢失,也方便后续把数据持久化(比如存到本地或服务器)。
- 方案二更适合快速临时解决问题,无需改动原有模型结构,但数据只存在视图的
@State里,视图销毁后数据会丢失。
内容的提问来源于stack exchange,提问作者Roubleman
相关产品推荐
相关产品推荐

