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

SwiftUI Timer结合NavigationLink:播放/暂停按钮工作异常排查

问题原因

传统的Timer.scheduledTimer依赖RunLoop运行,在NavigationStack这类容器视图中,TestAnimationView可能因为被系统离屏缓存、视图重建,导致Timer绑定的RunLoop不再活跃;再加上用@State存储Timer实例,SwiftUI的视图更新机制可能会让这个Timer引用意外丢失,最终出现随机停止的情况。另外你没处理onDisappear,视图离开屏幕时Timer没被正确清理,回来后状态容易混乱。

修复方案(最小改动)

用SwiftUI原生的Timer.publish结合Combine框架替代手动管理Timer,完全适配SwiftUI的响应式生命周期,不用重写整个视图:

修改后的代码:

import Combine // 别忘了引入Combine框架

struct TestAnimationView: View {
    @State private var frameIndex = 0
    @State private var isPlaying = false
    @State private var animationTimer: AnyCancellable? // 替换原来的Timer?
    
    let frames = ["frame1", "frame2", "frame3"]
    
    var body: some View {
        VStack(spacing: 24) {
            Text("Frame \(frameIndex)")
            Button(isPlaying ? "Pause" : "Play") {
                isPlaying.toggle()
                if isPlaying {
                    startAnimation()
                } else {
                    stopAnimation()
                }
            }
        }
        .onDisappear {
            stopAnimation() // 视图消失时确保停止Timer
        }
    }
    
    func startAnimation() {
        stopAnimation() // 先停止已有Timer,避免重复创建
        // 创建Timer Publisher,绑定到主线程RunLoop
        animationTimer = Timer.publish(every: 0.2, on: .main, in: .common)
            .autoconnect()
            .sink { _ in
                frameIndex = (frameIndex + 1) % frames.count
            }
    }
    
    func stopAnimation() {
        animationTimer?.cancel()
        animationTimer = nil
    }
}
改动说明
  • 用AnyCancellable存储Timer订阅,替代原来的Timer?,Combine会自动处理订阅的生命周期
  • Timer.publish明确绑定到主线程RunLoop,避免因视图层级变化导致RunLoop不活跃的问题
  • 添加onDisappear调用stopAnimation,确保视图离开时清理Timer,回来后状态正常
  • 启动Timer前先调用stopAnimation,防止重复创建多个Timer导致帧更新混乱

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.11 20:12:09