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

SwiftUI运行定时器时Alert无法正常dismiss的问题该如何解决?

问题根因

Timer每0.1秒触发@Published属性更新,会导致整个TimerView全局重绘,Alert的点击事件会和视图重绘时机冲突,单次点击触发的isAlertPresented状态变更没有被正确响应,所以需要多次点击才能关闭弹窗。

可行解决方案

方案1:拆分频繁刷新的子视图(最推荐)

把需要随Timer实时更新的计时文本拆分为独立子视图,只有子视图会随Timer更新重绘,父视图不会被频繁刷新,Alert绑定的状态就不会受冲突影响。就算按钮需要和Stopwatch对象交互,也可以正常传递对象,完全不影响业务逻辑。

拆分后的计时子视图代码:

// 仅负责计时展示的独立子视图
struct TimerDisplayView: View {
    @ObservedObject var stopwatch: Stopwatch
    var body: some View {
        Text(String(format: "%.1f", stopwatch.secondsElapsed))
            .font(.system(size: 70.0))
            .minimumScaleFactor(0.1)
            .lineLimit(1)
    }
}

修改后的主视图代码:

struct TimerView: View {
    // iOS14+ 用@StateObject持有ObservableObject比@ObservedObject更稳定
    @StateObject var stopwatch = Stopwatch()
    @State var isAlertPresented: Bool = false
    var body: some View {
        VStack{
            // 替换为拆分后的计时子视图
            TimerDisplayView(stopwatch: stopwatch)
            
            Button(action:{
                stopwatch.actionStartStop()
            }){
                Text("Toggle Timer")
            }
            
            Button(action:{
                do {
                    // 和Stopwatch交互的业务逻辑完全不受影响
                    try stopwatch.actionDoThis()
                } catch {
                    isAlertPresented = true
                }
            }){
                Text("Toggle Alert")
            }
        }
        .alert(isPresented: $isAlertPresented){
            Alert(title:Text("Error"),message:Text("I am presented"))
        }
    }
}

方案2:调整Timer的RunLoop运行模式

默认Timer添加在RunLoop的default模式下,用户交互时RunLoop会切换到tracking模式,Timer触发和交互事件会出现抢占。你可以在创建Timer时手动把它添加到common模式,避免交互时Timer阻塞,也能解决冲突。

修改Stopwatch的start方法即可:

func start() {
    secondsElapsed = 0.0
    mode = .running
    let timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
        self.secondsElapsed += 0.1
    }
    // 手动添加到common模式,兼容交互场景
    RunLoop.main.add(timer, forMode: .common)
    self.timer = timer
}

方案3:延迟更新Alert状态(兜底方案)

如果上述方案都不适用,可以在修改isAlertPresented状态时添加极短的异步延迟,确保交互事件不会和视图重绘冲突:

Button(action:{
    do{
        try stopwatch.actionDoThis()
    }catch{
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
            isAlertPresented = true
        }
    }
}){
    Text("Toggle Alert")
}

内容的提问来源于stack exchange,提问作者itsBryan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 11:15:03