如何使用RxSwift实现可重启的倒计时功能
解决RxSwift倒计时可重启且订阅不终止的问题
原代码的核心问题是take(counter + 1)作用于外层序列,导致倒计时结束后整个订阅直接完成终止。要实现点击按钮即可重启、且订阅持续活跃的效果,需要调整序列的结构,把终止逻辑限制在单次倒计时的内部序列中。
修改后的代码
fileprivate let counter = 10 fileprivate let startCountDown = PublishRelay<Void>() startCountDown .flatMapLatest { _ -> Observable<Int> in // 每次触发时生成独立的倒计时序列,内部完成不影响外层订阅 return Observable<Int>.timer(.seconds(0), period: .seconds(1), scheduler: MainScheduler.instance) .take(self.counter + 1) // 将take移到内部,仅终止当前单次倒计时 .map { self.counter - $0 } // 可选:如需从counter倒计数到0,添加此map } .subscribe(onNext: { time in print(time) }, onCompleted: { // 外层订阅不会触发此回调,因为startCountDown是永不自动完成的PublishRelay print("Completed") }) .disposed(by: rx.disposeBag)
关键调整说明
- 把
take移到内部序列:原代码中take作用于外层,会直接终止整个订阅。现在将其放在flatMapLatest返回的内部Observable里,每次倒计时结束仅终止当前的单次序列,外层的startCountDown订阅始终保持活跃,等待下一次触发。 - 利用
flatMapLatest的特性:每次点击按钮触发startCountDown时,会自动取消上一轮未完成的倒计时,立即启动新的倒计时,完美实现“重启”逻辑。 - 可选倒计数转换:如果需要显示从
counter到0的倒计数值(比如10、9...0),添加map { self.counter - $0 }即可;若保留原代码的0到10递增输出,可移除该map。
验证逻辑
- 首次点击:正常完成一轮倒计时,订阅保持活跃。
- 中途点击:当前倒计时被中断,立刻启动新的一轮倒计时。
内容的提问来源于stack exchange,提问作者ChanSeul
相关产品推荐
相关产品推荐

