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

如何通过onTapGesture重复连接Timer?首次点击生效后后续无法触发的问题求助

如何通过onTapGesture重复连接Timer?首次点击生效后后续无法触发的问题求助

嘿,我仔细捋了你的代码,一眼就揪出问题了——你定义的timer是个不可变的let常量,而且用的是Timer.publish创建的ConnectablePublisher。这种类型的发布器一旦调用cancel()之后,整个事件序列就直接"寿终正寝"了,后续再喊timer.connect()根本不会有反应,相当于你在给一个已经关机的机器按开机键,当然没动静啦。

给你拆解下当前的执行死循环:

  • 第一次点击时,timer.connect()成功启动计时器,5秒后你调用了timer.connect().cancel()——这里你取消的是当前的连接订阅,但原发布器已经走完生命周期进入完成状态了。
  • 第二次点击再调用timer.connect(),发布器已经发不出任何事件,自然你的timeOut函数也不会被触发。

下面给你两个靠谱的修复方案,选哪个看你习惯:

方案一:每次点击创建新计时器(更推荐,灵活度高)

这种方式不用复用旧计时器,每次点击都生成全新的,彻底避免复用带来的状态混乱:

  1. 删掉原来的let timer = Timer.publish(...),换成一个用来保存订阅的状态变量:
    @State private var timerCancellable: AnyCancellable?
    
  2. 删掉原来的.onReceive(timer)修饰符,把计时器的订阅逻辑移到onTapGesture里:
    .onTapGesture {
        isSorting = true
        count = 0 // 重置计数,避免上次残留数值影响
        // 先取消之前可能存在的计时器,防止多个计时器同时跑
        timerCancellable?.cancel()
        // 创建新的计时器并订阅事件
        timerCancellable = Timer.publish(every: 1, on: .main, in: .common)
            .autoconnect()
            .sink { [weak self] _ in
                self?.timeOut()
            }
    }
    
  3. 修改timeOut函数,不用再管timer的connect/cancel,直接到点后取消当前订阅:
    func timeOut() {
        count += 1
        if count >= 5 {
            isSorting = false
            timerCancellable?.cancel()
            timerCancellable = nil
            count = 0
        }
    }
    

方案二:复用原发布器,只管理连接的订阅

如果你想保留原来的Timer.publish定义,那可以只保存每次连接的订阅对象,不碰原发布器的生命周期:

  1. 保留原来的let timer = Timer.publish(every: 1, on: .main, in: .common),新增一个状态变量保存订阅:
    @State private var activeTimerSubscription: AnyCancellable?
    
  2. 修改onTapGesture逻辑,每次点击先取消旧订阅,再重新连接:
    .onTapGesture {
        isSorting = true
        count = 0 // 重置计数
        // 取消之前的连接
        activeTimerSubscription?.cancel()
        // 重新连接计时器并保存订阅
        activeTimerSubscription = timer.connect() as? AnyCancellable
    }
    
  3. 修正timeOut函数里的取消逻辑,只取消当前订阅不碰原发布器:
    func timeOut() {
        count += 1
        if count >= 5 {
            isSorting = false
            activeTimerSubscription?.cancel()
            activeTimerSubscription = nil
            count = 0
        }
    }
    
  4. 原来的.onReceive(timer)可以保留,因为我们还是用原发布器接收事件。

额外小提醒

你原代码里的timeOut还有个小bug:你是先判断count >=5,再执行count +=1,这会导致实际要等6秒才会关闭isSorting(第5次触发时count是4,判断不成立,加1到5;第6次触发才会触发关闭逻辑)。把count +=1移到判断前面,就能准确在5秒后触发啦。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 10:53:10