在Text的.onTapGesture中修改状态变量引发SwiftUI未定义行为错误
解决SwiftUI中点击按钮修改状态引发的视图更新错误
你遇到的问题是因为在onTapGesture闭包中直接修改状态变量时,这些变量的变化会立即触发视图重新渲染,而此时当前的视图更新周期还没结束,从而导致“Modifying state during view update”的错误。
解决方案
将状态修改的代码包裹在DispatchQueue.main.async中,把状态更新推迟到当前视图更新周期完成后再执行,避免和视图渲染过程冲突。
修改后的代码示例:
Text("Show next time") .font(.system(size: buttonFontSize, weight: .heavy, design: .rounded)) .padding() .background(RoundedRectangle(cornerRadius: 10, style: .continuous).fill(showNextTimeDisabled ? .gray : Color.systemBlue)) .foregroundColor(.white) .opacity(showNextTimeDisabled ? 0.8 : 1.0) .disabled(showNextTimeDisabled) .frame(width: geometry.size.width * buttonWidthScale, height: geometry.size.height * buttonHeightScale) .position(x: geometry.frame(in: .local).midX + geometry.size.width / 3.9, y: geometry.frame(in: .local).midY + geometry.size.height / 26) .onTapGesture { spacedRepetition.decreaseTestsUntilPresented(currentQuestion: testSectionThings?[questionCounter] ?? SectionThing()) // 用DispatchQueue.main.async包裹状态修改 DispatchQueue.main.async { showNextTimeDisabled = true iKnowThisDisabled = true answerDisabled = true skipDisabled = false } if answered { testSectionThings?[questionCounter].correctAnswers = 0 testSectionThings?[questionCounter].attempts += 1 PersistenceController.shared.saveDB() } if wrongAnswer.isEmpty { let separator = NSMutableAttributedString(string: " ", attributes: noTestSectionThingsAttributes) let tapAttributedString = NSMutableAttributedString(string: "Tap ", attributes: noTestSectionThingsAttributes) let skipImageAttachment = NSTextAttachment() skipImageAttachment.image = UIImage(named: "skip.png")?.withTintColor(.systemBlue) let skipImageAttributedString = NSMutableAttributedString(attachment: skipImageAttachment) let nextQuestionAttributedString = NSMutableAttributedString(string: NSLocalizedString(" for next question.", comment: "Answer field tap skip 2"), attributes: noTestSectionThingsAttributes) let NSMutableAttributedStringArray = [tapAttributedString, skipImageAttributedString, nextQuestionAttributedString] nativeForeignPlaceholder = NSMutableAttributedStringArray.join(withSeparator: separator) } }
原理说明
SwiftUI的视图更新是同步进行的,当你在手势闭包中直接修改状态变量,会立即触发视图的body重新计算。如果此时闭包还在执行,就会出现“在视图更新期间修改状态”的冲突。通过DispatchQueue.main.async,我们把状态更新的操作放到主线程的下一个运行循环中,等当前的视图渲染完成后再执行状态修改,就能彻底避免这个错误。
内容的提问来源于stack exchange,提问作者Tycho Systems
相关产品推荐
相关产品推荐

