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
相关产品推荐
相关产品推荐

