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

SwiftUI中如何让Timer保持精准且不改动现有代码结构?

精准计时解决方案(保留onReceive结构)

核心思路

问题根源在于你依赖Timer的触发时间计算训练时长——Timer的触发会受主线程负载影响产生延迟,0.001秒的高频间隔更是会加剧这个问题。正确的做法是记录训练会话的起始绝对时间,每次Timer触发时用当前时间减去起始时间得到真实流逝时长,完全规避Timer延迟带来的误差。

修改后的代码实现

struct TrainingSessionView: View {
    // 保留原Timer定义,建议将间隔调至屏幕刷新率级别(0.016秒≈60fps,兼顾流畅度与性能)
    let timer = Timer
        .publish(every: 0.016, on: .main, in: .common)
        .autoconnect()
    
    // 记录训练起始时间
    @State private var startTime: Date?
    // 存储精准计算的流逝时长
    @State private var elapsedTime: TimeInterval = 0
    
    var body: some View {
        VStack {
            // 基于elapsedTime更新你的各类图形元素,比如显示剩余时长/击打数据
            Text(String(format: "已训练:%.2f秒", elapsedTime))
        }
        .onReceive(timer) { _ in
            // 忽略Timer传入的时间参数,用系统当前时间计算真实流逝时长
            guard let start = startTime else { return }
            elapsedTime = Date().timeIntervalSince(start)
            
            // 在这里放置你的各类计算逻辑(基于elapsedTime而非Timer触发时间)
            
            // 示例:训练时长达到1分钟时停止会话
            if elapsedTime >= 60 {
                timer.upstream.connect().cancel()
                startTime = nil
            }
        }
        .onAppear {
            // 训练开始时初始化起始时间
            startTime = Date()
        }
    }
}

关键优化点

  • 抛弃Timer触发时间依赖:所有时长计算基于Date().timeIntervalSince(startTime),这个值是系统级的精准时间,不受Timer延迟影响。
  • 降低Timer触发频率:0.001秒的间隔远超过UI渲染极限(屏幕刷新率多为60/120Hz),不仅浪费资源还会导致主线程卡顿,调至0.016秒完全满足UI更新需求。
  • 保留原有代码结构:无需引入ObservableObject或TimelineView,仅通过@State管理起始时间,在原有的onReceive闭包里完成精准计算。

原方案不准的原因

Timer的every: 0.001只是向系统发起触发请求,但主线程如果在处理传感器数据、UI渲染等任务,Timer的触发会被延迟。如果你的代码基于每次触发的time参数累加时长,这些延迟会被累积,最终导致1分钟的训练实际耗时偏长。而基于起始时间的计算,不管Timer什么时候触发,都能拿到真实的流逝时长。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 12:43:20