2017-02-18 13 views
2

Ich habe drei Knöpfe und ich möchte, dass sie nur eine zu einem Zeitpunkt ausgewählt werden:RxSwift und isSelected Eigenschaft auf UIButton

enter image description here

und:

enter image description here

etc ...

Mein Ansatz ist dies:

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) 
} 

Gibt es einen besseren Ansatz? Es funktioniert, aber gibt es einen besseren "reaktiven" Weg, es mit RxSwift zu machen?

Antwort

10

Subject und durch Erweiterung Variable sind die meiste Zeit nur sinnvoll, wenn von Imperativ zu reaktive Welt überbrückt wird. Hier könnte man ohne sie auskommen.

.do(onNext:) ist auch eine Möglichkeit, Nebeneffekt, etwas, was Sie normalerweise nicht in Ihrem reaktiven Code wollen.

// 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) 
+0

Vielen Dank für die Antwort. Ich bin ziemlich neu in der reaktiven Programmierung. Der Operator zu reduzieren ist ein bisschen schwierig für mich zu erfassen. Verfügen Sie über erläuternde Ressourcen für RxSwift im Allgemeinen (oder speziell für den Reduce Operator), um mein Wissen zu erweitern? – TheoK

+0

'reduce' ist eigentlich eine regelmäßige schnelle Sequenzmethode. Es braucht einen Startwert und einen Akkumulator. Das einfachste Beispiel ist "[1, 2, 3] .reduce (0) {$ 0 + $ 1}". Dies berechnet '((0 + 1) + 2) + 3 = 6 '. In unserem Fall kann man sich das Ergebnis als "Disposable.create (Disposable.create (Disposable.create (emptyDisposable, button1Disposable), button2Disposable), button3Disposable)' – tomahh

+0

Great @tomahh vorstellen. Wenn ich richtig verstanden habe, gibt Ihnen die Methode reduce die Möglichkeit, eine Funktion auf jedes Element des Arrays anzuwenden und dann ein einzelnes Abonnement zu erstellen, statt eines für jedes Element zu erstellen, richtig? Und der Grund, warum Sie ein Disposable zurückgeben, liegt daran, dass die bindTo-Methode ein Disposable erstellt? Die Umsetzung im Inneren reduziert mich ein wenig. (Sorry für die Fragen, aber ich möchte die Begründung hinter der Implementierung verstehen :)) – TheoK