2017-05-04 7 views
1

Je crée une sous-classe personnalisée de UIControl (je dois remplacer la méthode de tirage au sort) et je veux ajouter RxSwift pour lier son isSelected bien à mon modèle.sous-classe personnalisée UIControl avec RxSwift

Jusqu'ici tout va bien. Cela fonctionne bien.

Mon problème est comment puis-je faire pour changer la valeur isSelected propriété en réponse à l'événement touchUpInside utilisateur ?.

Mon premier essai était d'utiliser la méthode addTarget de UIControl, mais en changeant la valeur de isSelected est pas signalé par le programme ControlProperty (comme indiqué dans le document). Mais je peux trouver une autre façon de résoudre ce problème.

Toute aide appréciée.

code source de la sous-classe:

class SYYesNoButton: UIControl { 

override init(frame: CGRect) { 
    super.init(frame: frame) 

    // subscribe to touchUpInside event 

    addTarget(
     self, 
     action: #selector(userDidTouchUpInside), 
     for: UIControlEvents.touchUpInside) 
} 



func userDidTouchUpInside() { 

    // change the value of the property 
    // this does not work, 
    // the change is not reported to the ControlProperty 
    // HOW CAN I CHANGE THIS ?? 

    self.isSelected = !isSelected 
} 

} 

extensions pour ajouter le support réactif:

extension SYYesNoButton { 

    var rx_isSelected: ControlProperty<Bool> { 

    return UIControl.valuePublic(
     self, 
     getter: { (button) -> Bool in 
      return button.isSelected 
    }, 
     setter: { (button, value) in 
      button.isSelected = value 
    }) 


    } 
} 



extension UIControl { 

    static func valuePublic<T, ControlType: UIControl>(_ control: ControlType, getter: @escaping (ControlType) -> T, setter: @escaping (ControlType, T) ->()) -> ControlProperty<T> { 



     let values: Observable<T> = Observable.deferred { [weak control] in 
      guard let existingSelf = control else { 
       return Observable.empty() 
      } 

      return (existingSelf as UIControl).rx.controlEvent([.allEditingEvents, .valueChanged]) 
       .flatMap { _ in 
        return control.map { Observable.just(getter($0)) } ?? Observable.empty() 
       } 
       .startWith(getter(existingSelf)) 
     } 


     return ControlProperty(values: values, valueSink: UIBindingObserver(UIElement: control) { control, value in 
      setter(control, value) 
     }) 

    } 
} 

Merci pour tous.

+0

êtes-vous aussi en utilisant 'RxCocoa'? – ozgur

+0

J'utilise aussi RxCocoa – t4ncr3d3

Répondre

2

Si vous UIControl de sous-classement, alors vous faites votre propre classe de contrôle et vous devez remplacer un ou plusieurs des beginTracking(_:with:), continueTracking(_:with:), endTracking(_:with:) ou cancelTracking(with:) pour faire le travail de contrôle de la façon dont vous voulez. Puis appelez sendActions(for:) avec l'événement correct. Le courage d'un UIControl n'aurait pas Rx dedans.

Prendre une file d'attente UIButton, votre commande ne devrait pas sélectionner lui-même, bien qu'il puisse mettre en évidence et pas surligner se

(lorsque le doigt de l'utilisateur est sur elle par exemple.) Une fois que vous avez correctement créé votre UIControl, le code En dehors de le contrôle peut utiliser Rx pour l'observer sans travail supplémentaire de votre part.

Les travaux suivants:

class ViewController: UIViewController { 

    @IBOutlet weak var yesNoButton: SYYesNoButton! 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     yesNoButton.rx.controlEvent(.touchUpInside) 
      .scan(false, accumulator: { !$0.0 }) 
      .bind(to: yesNoButton.rx.isSelected) 
      .disposed(by: bag) 
    } 

    let bag = DisposeBag() 
} 

@IBDesignable 
class SYYesNoButton: UIControl { 

    override func layoutSubviews() { 
     super.layoutSubviews() 
     backgroundColor = isSelected ? .green : .red 
    } 

    override var isSelected: Bool { 
     didSet { 
      super.isSelected = isSelected 
      backgroundColor = isSelected ? .green : .red 
     } 
    } 
}