问题
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 anObservable<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