【问题标题】:How to make restartable countdown using Rxswift如何使用 Rxswift 进行可重启的倒计时
【发布时间】:2023-01-09 20:38:38
【问题描述】:

我想创建一个倒计时功能,每次按下按钮时都会重新启动。 但是,我编写的代码会在倒计时结束时终止订阅。 我该怎么做才能确保我的订阅不会被终止并重新开始倒计时?

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(counter + 1)
      .subscribe(onNext: { time in
        print(time)
      }, onCompleted: {
        print("Completed")
      })
      .disposed(by: rx.disposeBag)

【问题讨论】:

  • 请提供足够的代码,以便其他人可以更好地理解或重现问题。

标签: ios rx-swift


【解决方案1】:

take(_:) 完成时,Observable 链被释放。但是,如果 flatMapLatest 闭包内的 Observable 完成,则链不会被释放,因为 startCountDown 尚未完成。 flatMapLatest observable 在它订阅的所有 observable 完成之前不会完成。所以解决方案是将 take(_:) 放在 flatMapLatest 中。

视图模型应如下所示:

func startCountDown(counter: Int, trigger: Observable<Void>, scheduler: SchedulerType) -> Observable<Int> {
    trigger
        .flatMapLatest {
            Observable<Int>.timer(.seconds(0), period: .seconds(1), scheduler: scheduler)
                .take(counter + 1)
        }
}

您在视图控制器中使用上述视图模型,如下所示:

startCountdown(counter: 10, trigger: startButton.rx.tap.asObservable(), scheduler: MainScheduler.instance)
    .subscribe(onNext: { time in
        print(time)
    })
    .disposed(by: rx.disposeBag)

严格来说,这是一个计时计时器。它将从 0 变为 counter,然后等待按钮再次被点击。如果在计数时点击按钮,它将重新启动。如果您希望它在完成计数之前忽略水龙头,请改用flatMapFirst

通过阅读本文了解更多信息:RxSwift's Many Faces of FlatMap


作为奖励,以下是测试视图模型的方法:

final class CountdownTests: XCTestCase {
    func test() {
        let scheduler = TestScheduler(initialClock: 0)
        let trigger = scheduler.createObservable(timeline: "--V---V-|", values: ["V": ()])
        let expected = parseEventsAndTimes(timeline:       "---012-0123456789|", values: { Int(String($0))! })
        let result = scheduler.start(created: 0, subscribed: 0, disposed: 100) {
            startCountDown(counter: 9, trigger: trigger, scheduler: scheduler)
        }
        XCTAssertEqual(result.events, expected[0])
    }
}

以上使用我的TestScheduler

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 1970-01-01
    • 2013-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多