如何通过onTapGesture重复连接Timer?首次点击生效后后续无法触发的问题求助
如何通过onTapGesture重复连接Timer?首次点击生效后后续无法触发的问题求助
嘿,我仔细捋了你的代码,一眼就揪出问题了——你定义的timer是个不可变的let常量,而且用的是Timer.publish创建的ConnectablePublisher。这种类型的发布器一旦调用cancel()之后,整个事件序列就直接"寿终正寝"了,后续再喊timer.connect()根本不会有反应,相当于你在给一个已经关机的机器按开机键,当然没动静啦。
给你拆解下当前的执行死循环:
- 第一次点击时,
timer.connect()成功启动计时器,5秒后你调用了timer.connect().cancel()——这里你取消的是当前的连接订阅,但原发布器已经走完生命周期进入完成状态了。 - 第二次点击再调用
timer.connect(),发布器已经发不出任何事件,自然你的timeOut函数也不会被触发。
下面给你两个靠谱的修复方案,选哪个看你习惯:
方案一:每次点击创建新计时器(更推荐,灵活度高)
这种方式不用复用旧计时器,每次点击都生成全新的,彻底避免复用带来的状态混乱:
- 删掉原来的
let timer = Timer.publish(...),换成一个用来保存订阅的状态变量:@State private var timerCancellable: AnyCancellable? - 删掉原来的
.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() } } - 修改
timeOut函数,不用再管timer的connect/cancel,直接到点后取消当前订阅:func timeOut() { count += 1 if count >= 5 { isSorting = false timerCancellable?.cancel() timerCancellable = nil count = 0 } }
方案二:复用原发布器,只管理连接的订阅
如果你想保留原来的Timer.publish定义,那可以只保存每次连接的订阅对象,不碰原发布器的生命周期:
- 保留原来的
let timer = Timer.publish(every: 1, on: .main, in: .common),新增一个状态变量保存订阅:@State private var activeTimerSubscription: AnyCancellable? - 修改
onTapGesture逻辑,每次点击先取消旧订阅,再重新连接:.onTapGesture { isSorting = true count = 0 // 重置计数 // 取消之前的连接 activeTimerSubscription?.cancel() // 重新连接计时器并保存订阅 activeTimerSubscription = timer.connect() as? AnyCancellable } - 修正
timeOut函数里的取消逻辑,只取消当前订阅不碰原发布器:func timeOut() { count += 1 if count >= 5 { isSorting = false activeTimerSubscription?.cancel() activeTimerSubscription = nil count = 0 } } - 原来的
.onReceive(timer)可以保留,因为我们还是用原发布器接收事件。
额外小提醒
你原代码里的timeOut还有个小bug:你是先判断count >=5,再执行count +=1,这会导致实际要等6秒才会关闭isSorting(第5次触发时count是4,判断不成立,加1到5;第6次触发才会触发关闭逻辑)。把count +=1移到判断前面,就能准确在5秒后触发啦。
内容来源于stack exchange
相关产品推荐
相关产品推荐

