I have three buttons and I want them to be selected only one at a time:
and:
etc...
My approach is this:
class MyController: UIViewController {
@IBOutlet var buttonOne: UIButton!
@IBOutlet var buttonTwo: UIButton!
@IBOutlet var buttonThree: UIButton!
var buttonOneIsSelected = Variable(true)
var buttonTwoIsSelected = Variable(false)
var buttonThreeIsSelected = Variable(false)
override func viewDidLoad() {
super.viewDidLoad()
buttonOne.isSelected = true
buttonOneIsSelected.asDriver()
.drive(buttonOne.rx.isSelected)
.disposed(by: disposeBag)
buttonTwoIsSelected.asDriver()
.drive(buttonTwo.rx.isSelected)
.disposed(by: disposeBag)
buttonThreeIsSelected.asDriver()
.drive(buttonThree.rx.isSelected)
.disposed(by: disposeBag)
buttonOne.rx.tap.asObservable().map { (_) -> Bool in
return !self.buttonOne.isSelected
}
.do(onNext: { (isSelected) in
self.buttonTwoIsSelected.value = !isSelected
self.buttonThreeIsSelected.value = !isSelected
})
.bindTo(buttonOne.rx.isSelected)
.disposed(by: disposeBag)
buttonTwo.rx.tap.asObservable().map { (_) -> Bool in
return !self.buttonTwo.isSelected
}
.do(onNext: { (isSelected) in
self.buttonOneIsSelected.value = !isSelected
self.buttonThreeIsSelected.value = !isSelected
})
.bindTo(buttonTwo.rx.isSelected)
.disposed(by: disposeBag)
buttonThree.rx.tap.asObservable().map { (_) -> Bool in
return !self.buttonThree.isSelected
}
.do(onNext: { (isSelected) in
self.buttonOneIsSelected.value = !isSelected
self.buttonTwoIsSelected.value = !isSelected
})
.bindTo(buttonThree.rx.isSelected)
.disposed(by: disposeBag)
}
Is there a better approach? It works, but is there a better 'reactive' way to do it by using RxSwift?
Subject
and by extension Variable
are most of the time only useful when bridging from imperative to reactive world. Here, you could do without them.
.do(onNext:)
is also a way to perform side effect, something you usually don't want in your reactive code.
// force unwrap to avoid having to deal with optionals later on
let buttons = [button1, button2, button3].map { $0! }
// create an observable that will emit the last tapped button (which is
// the one we want selected)
let selectedButton = Observable.from(
buttons.map { button in button.rx.tap.map { button } }
).merge()
// for each button, create a subscription that will set its `isSelected`
// state on or off if it is the one emmited by selectedButton
buttons.reduce(Disposables.create()) { disposable, button in
let subscription = selectedButton.map { $0 == button }
.bindTo(button.rx.isSelected)
// combine two disposable together so that we can simply call
// .dispose() and the result of reduce if we want to stop all
// subscriptions
return Disposables.create(disposable, subscription)
}
.addDisposableTo(disposeBag)
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