I want to prevent my UIButtons from emitting RX tap events until some control stream indicates that it is ready. I want buttons to be disabled again if the control stream reverts back to not ready. Control sequence is a ReplaySubject
and always has value
I've tried skipUntil
, but that's a one shot operation - once the control sequence emits, it cannot go back to disabled state - all button presses will go through, ignoring the control sequence
How can I restrict a UIButton from sending tap events unless some other stream has a certain value?
let enableButtons = configStatusInputSequence
.filter { (configured, ready) -> Bool in
return configured && ready
}
for button in controlButtons{
button.rx.tap
.skipUntil(enableButtons)
.bind(to: commandOutputSequence)
.disposed(by: bag)
}
Here's something I wrote a while back and reproduced below. If you look at my gist (https://gist.github.com/danielt1263/1a70c4f7b8960d06bd7f1bfa81802cc3) you will see that I originally wrote it as a custom operator. Later I learned that the combination of built in operators below do the same job.
If nothing else, looking back at the older revisions in the gist will give you the sense of how to write your own operators.
extension ObservableType {
/**
Filters the source observable sequence using a trigger observable sequence producing Bool values.
Elements only go through the filter when the trigger has not completed and its last element was true. If either source or trigger error's, then the source errors.
- parameter trigger: Triggering event sequence.
- returns: Filtered observable sequence.
*/
func filter(if trigger: Observable<Bool>) -> Observable<E> {
return withLatestFrom(trigger) { (myValue, triggerValue) -> (Element, Bool) in
return (myValue, triggerValue)
}
.filter { (myValue, triggerValue) -> Bool in
return triggerValue == true
}
.map { (myValue, triggerValue) -> Element in
return myValue
}
}
}
If you want to change what the button does depending on the value of your control observable, setup two filters. Normally the filter only passes through taps when the enableButtons
emits true. Use a map to reverse it in the second case and direct button taps down another path:
button.rx.tap.filter(if: enableButtons)
.subscribe(onNext: { /* do one thing when enableButtons emits true */ }
.disposed(by: bag)
button.rx.tap.filter(if: enableButtons.map { !$0 })
.subscribe(onNext: { /* do other thing when enable buttons emits false*/ }
.disposed(by: bag)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With