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

Swift中如何基于计算型布尔变量启停Timer?

Swift中基于计算型布尔变量启停Timer的解决方案

问题背景

需要在SwiftUI视图中,根据计算型布尔变量isActive(判断逻辑为fetcher.currentItemId == item.id)控制Timer的启停。当前代码能实现Timer的取消,但无法重新创建启动Timer。

原代码

struct TestView: View {
    @EnvironmentObject var fetcher: TestFetcher
    
    var item: Item
    var isActive: Bool {
        fetcher.currentItemId == item.id
    }
    
    @State private var counter = 0
    @State private var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    var body: some View {
        HStack {
            Text("\(counter)")
                .onReceive(timer) { _ in
                    if (isActive) {
                        counter += 1
                    } else {
                        timer.upstream.connect().cancel()
                    }
                }
        }
    }
}

尝试过的错误方式

方式1:将Timer设为计算属性

var timer: Any? {
    if (fetcher.currentItemId == item.id) {
        return Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    } else {
        return timer.connect().cancel()
    }
}

报错信息:

Instance method 'onReceive(_:perform:)' requires that 'Any?' conform to 'Publisher'

问题原因:计算属性返回Any?类型,不符合onReceive要求的Publisher类型,且cancel()无返回值,逻辑写法错误。

方式2:在计算属性isActive中修改Timer

var isActive: Bool {
    let _bool = fetcher.currentItemId == item.id
    
    if (_bool) {
        self.timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    } else {
        self.timer.connect().cancel()
    }
    
    return _bool
}

无效原因:计算属性不能修改自身或其他存储属性,也没有didSet监听回调,无法触发Timer的创建逻辑。

正确实现方案

使用AnyCancellable管理Timer的订阅生命周期,配合.onChange监听isActive的变化来启停Timer:

import Combine
import SwiftUI

struct TestView: View {
    @EnvironmentObject var fetcher: TestFetcher
    var item: Item
    
    var isActive: Bool {
        fetcher.currentItemId == item.id
    }
    
    @State private var counter = 0
    // 用AnyCancellable存储Timer的订阅
    @State private var timerCancellable: AnyCancellable?
    
    var body: some View {
        HStack {
            Text("\(counter)")
        }
        .onChange(of: isActive) { newIsActive in
            if newIsActive {
                // 启动Timer:创建Timer并订阅,保存订阅对象
                timerCancellable = Timer.publish(every: 1, on: .main, in: .common)
                    .autoconnect()
                    .sink { _ in
                        counter += 1
                    }
            } else {
                // 停止Timer:取消订阅
                timerCancellable?.cancel()
                timerCancellable = nil
            }
        }
        // 视图初始化时,根据初始isActive状态设置Timer
        .onAppear {
            if isActive {
                timerCancellable = Timer.publish(every: 1, on: .main, in: .common)
                    .autoconnect()
                    .sink { _ in
                        counter += 1
                    }
            }
        }
    }
}

逻辑说明

  1. AnyCancellable管理订阅:Timer.publish返回Publisher,通过sink订阅后得到AnyCancellable,保存该对象即可控制Timer生命周期,调用cancel()就能停止Timer。
  2. .onChange监听状态变化:当isActive值改变时,根据新状态决定创建订阅Timer或取消订阅。
  3. .onAppear初始化状态:视图首次出现时,根据初始isActive状态决定是否启动Timer。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 03:30:40