How to detect if a observable has not emitted any events for specific time in RxSwift

给你一囗甜甜゛ 提交于 2019-12-06 14:36:37

问题


I am trying to detect if a observable(my case button.rx.tap) has not emitted any value for say like 3 seconds. If yes, I would like to update the user interface. Here is my attempt so far:

Observable<Int>.interval(3, scheduler: MainScheduler.instance)
    .takeUntil(button.rx.tap) // I know take until will stop the timer sequence 
    .subscribe({ event in
        print(event)
        UIView.animate(withDuration: 0.4, animations: {
            if let number = event.element {
                let scale: CGFloat = number % 2 == 0 ? 1.5 : 1.0
                self.button.transform = CGAffineTransform(scaleX: scale, y: scale)
            }
        })
    })
    .addDisposableTo(disposeBag)

My goal is to animate the view whenever the button is not tapped for three seconds. I have tried scan, distinctUntilChanged and debounce but most combining operators I have encountered will emit items only when an item is emitted by a observable. Any help is much appreciated.


回答1:


enum Event {
case tap
case timeout
}

let tapOrTimeout = button.rx.tap
  .map { _ in Event.tap }
  .timeout(3, scheduler: MainScheduler.instance)
  .catchErrorJustReturn(.timeout)

Observable.repeatElement(tapOrTimeout).concat()
  .subscribe(onNext: { event in
    switch event {
    case .tap: tapHandler()
    case .timeout: callForAttention()
    }
  })
  • timeout(_:scheduler:) will raise an error if no event comes out of the chain within 3 seconds.
  • catchErrorJustReturn(_:) will transform the error into a .timeout event
  • at this point, if the observable times out, it will also complete, hence nothing will happen afterward. Using Observable.repeatElement(_:).concat() we first create an Observable<Observable<Event>> and then concatenate inner observables. Which in our case means that we'll subscribe to the first, and the resubscribe to the same observable if the first completes.

If we had prefer to only play the callForAttention() animation once, we could have done the following

let tap = button.rx.tap
  .map { _ in Event.tap }

let tapOrTimeout = tap  
  .timeout(3, scheduler: MainScheduler.instance)
  .catchErrorJustReturn(.timeout)

Observable.of(tapOrTimeout, tap).concat()
  .subscribe(onNext: { /* same as above */})

This way, we first timeout, but then only emit an event when a tap occurs.



来源:https://stackoverflow.com/questions/42145623/how-to-detect-if-a-observable-has-not-emitted-any-events-for-specific-time-in-rx

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!