You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 19:50:21