Swift中LazyVGrid切换题目后答案视图不更新问题求助
问题排查与解决
问题现象
手动修改questionNumber属性时,界面能正常显示对应题目和答案;但点击“Next Question”按钮切换题目时,题目文本更新正常,可TestAnswerTest视图未同步更新为新题目选项。不过在答案视图的点击手势中打印correctAnswers数组,显示的是当前题目的正确答案。
核心原因
- 子视图的
@State误用:TestAnswerTest中的answer被声明为@State,@State会让视图持有该属性的内部状态,当父视图传递新的answer值时,子视图会优先保留自己持有的旧状态,而不是更新为新值。 - ForEach的id不唯一:
LazyVGrid里的ForEach使用了id:\.self(即选项的索引),当不同题目选项数量相同时,SwiftUI会复用之前的TestAnswerTest实例,而不是创建新的实例,导致旧的answer值被保留。
解决方案
1. 修正子视图的状态声明
把TestAnswerTest中的@State var answer改为普通的let answer,因为answer是由父视图传递的外部数据,不需要子视图维护内部状态:
struct TestAnswerTest: View { let answer: String // 去掉@State修饰符 @State private var selected = false var correctAnswers: [String] var body: some View { Text(answer) .font(.title) .bold() .frame(width: 165, height: 140) .contentShape(RoundedRectangle(cornerRadius: 25)) .onTapGesture { self.selected.toggle() } } }
2. 给ForEach设置唯一的id
为了让SwiftUI能准确识别每个选项的唯一性,避免视图复用,修改ForEach的标识逻辑:
可以给每个TestAnswerTest添加唯一id,用当前题目id加选项索引的组合:
LazyVGrid(columns: twoColumnGrid) { ForEach(0..<staffQuestions[questionNumber].options, id: \.self) { number in TestAnswerTest( answer: staffQuestions[questionNumber].answers[number], correctAnswers: staffQuestions[questionNumber].correctAnswers ) .id("\(staffQuestions[questionNumber].id)-\(number)") // 添加唯一标识 } }
或者直接遍历answers数组,利用答案文本作为id(需确保题目内答案不重复):
LazyVGrid(columns: twoColumnGrid) { ForEach(staffQuestions[questionNumber].answers, id: \.self) { answer in TestAnswerTest( answer: answer, correctAnswers: staffQuestions[questionNumber].correctAnswers ) } }
额外优化建议
- 提前过滤
staff类题目,避免每次视图刷新都重复执行过滤逻辑:
struct StaffRecap2: View { @State private var questionNumber = 0 private let twoColumnGrid = [GridItem(.flexible()), GridItem(.flexible())] let questions:[Question2] = Bundle.main.decode("questions.json") private let staffQuestions: [Question2] init() { staffQuestions = questions.filter { $0.category == "staff" } } // 剩余代码不变... }
- 修正按钮的禁用条件,避免数组越界崩溃:
Button { questionNumber += 1 print(staffQuestions[questionNumber]) } label: { Text("Next Question") } .disabled(questionNumber >= staffQuestions.count - 1) // 修改为当前题目是最后一题时禁用
内容的提问来源于stack exchange,提问作者Chris Bromley
相关产品推荐
相关产品推荐

