2016-05-28 16 views
5

Ho letto l'operatore di collegamento bidirezionale nel codice di esempio di RxSwift.Associazione a due vie in RxSwift

func <-> <T>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable { 
    let bindToUIDisposable = variable.asObservable() 
     .bindTo(property) 
    let bindToVariable = property 
     .subscribe(onNext: { n in 
      variable.value = n 
     }, onCompleted: { 
      bindToUIDisposable.dispose() 
     }) 

    return StableCompositeDisposable.create(bindToUIDisposable, bindToVariable) 
} 

Quando property cambiato, ne informa variabile, e impostare il valore della variabile, mentre il valore della variabile è impostata, ne informa la proprietà. Penso che porterà a ciclo infinito ...

risposta

5

Grazie per sollevare la questione, ho trascorso qualche tempo a scavare intorno al ControlProperty implementazione (nota ho aggiunto una chiamata .debug() per tracciare i valori generati per la proprietà di controllo).

public struct ControlProperty<PropertyType> : ControlPropertyType { 
    public typealias E = PropertyType 

    let _values: Observable<PropertyType> 
    let _valueSink: AnyObserver<PropertyType> 

    public init<V: ObservableType, S: ObserverType where E == V.E, E == S.E>(values: V, valueSink: S) { 
     _values = values.debug("Control property values").subscribeOn(ConcurrentMainScheduler.instance) 
     _valueSink = valueSink.asObserver() 
    } 

    public func on(event: Event<E>) { 
     switch event { 
     case .Error(let error): 
      bindingErrorToInterface(error) 
     case .Next: 
      _valueSink.on(event) 
     case .Completed: 
      _valueSink.on(event) 
     } 
    } 
} 

La mia configurazione di prova era come seguendo, ho rimosso tutte le viste posizionamento qui per renderlo più breve:

import UIKit 
import RxSwift 
import RxCocoa 
class ViewController: UIViewController { 
    let variable = Variable<Bool>(false); 
    let bag = DisposeBag(); 

    override func loadView() { 
     super.loadView() 

     let aSwitch = UISwitch(); 
     view.addSubview(aSwitch) 

     (aSwitch.rx_value <-> variable).addDisposableTo(bag); 

     let button = UIButton(); 
     button.rx_tap.subscribeNext { [weak self] in 
      self?.variable.value = true; 
     }.addDisposableTo(bag) 
     view.addSubview(button); 
    } 
} 

infix operator <-> { 
} 

func <-> <T>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable{ 
    let bindToUIDisposable = variable.asObservable().debug("Variable values in bind") 
    .bindTo(property) 

    let bindToVariable = property 
     .debug("Property values in bind") 
     .subscribe(onNext: { n in 
      variable.value = n 
      }, onCompleted: { 
       bindToUIDisposable.dispose() 
     }) 

    return StableCompositeDisposable.create(bindToUIDisposable, bindToVariable) 
} 

Ora per i risultati. Per prima cosa proviamo a premere il pulsante, che dovrebbe impostare la variabile su true. Ciò attiva on(event: Event<E>) in ControlProperty e imposta il valore dello switch su true.

2016-05-28 12:24:33.229: Variable values in bind -> Event Next(true) 

// value flow 
value assigned to Variable -> 
Variable emits event -> 
ControlProperty receives event -> 
value assigned to underlying control property (e.g. `on` for `UISwitch`) 

Successivamente consente di attivare l'interruttore stesso. Quindi, come possiamo vedere, il controllo ha generato un evento come risultato di UIControlEventValueChanged che è stato passato attraverso _values in ControlProperty e quindi il suo valore è stato assegnato al valore Variable come nell'esempio sopra. Ma non c'è un ciclo, poiché l'aggiornamento al valore Variable non attiva un evento di controllo sullo switch.

2016-05-28 12:29:01.957: Control property values -> Event Next(false) 
2016-05-28 12:29:01.957: Property values in bind -> Event Next(false) 
2016-05-28 12:29:01.958: Variable values in bind -> Event Next(false) 

// value flow 
trigger the state of control (e.g. `UISwitch`) -> 
ControlProperty emits event -> 
value assigned to Variable -> 
Variable emits event -> 
ControlProperty receives event -> 
value assigned to underlying control property (e.g. `on` for `UISwitch`) 

Quindi una spiegazione semplice sarebbe:

  • un valore da un controllo viene emesso una volta una sorta di UIControlEvent viene attivato
  • quando un valore è assegnato direttamente alla proprietà di controllo, il controllo non innesca un evento di cambiamento quindi non c'è loop.

Speranza che aiuta, mi spiace per una spiegazione disordinato po '- ho trovato da esperimento)

10

Credo che si può semplicemente utilizzare bindTo. Qui ci sono implementazioni per ControlProperty <-> Variable e Variable <-> Variable:

infix operator <-> { precedence 130 associativity left } 

func <-><T: Comparable>(property: ControlProperty<T>, variable: Variable<T>) -> Disposable { 
    let variableToProperty = variable.asObservable() 
     .distinctUntilChanged() 
     .bindTo(property) 

    let propertyToVariable = property 
     .distinctUntilChanged() 
     .bindTo(variable) 

    return StableCompositeDisposable.create(variableToProperty, propertyToVariable) 
} 

func <-><T: Comparable>(left: Variable<T>, right: Variable<T>) -> Disposable { 
    let leftToRight = left.asObservable() 
     .distinctUntilChanged() 
     .bindTo(right) 

    let rightToLeft = right.asObservable() 
     .distinctUntilChanged() 
     .bindTo(left) 

    return StableCompositeDisposable.create(leftToRight, rightToLeft) 
} 

Esempi di ControlProperty <-> Variable (come UITextField e UITextView) sono nella RxSwiftPlayer project

// Example of Variable <-> Variable 

let disposeBag = DisposeBag() 
let var1 = Variable(1) 
let var2 = Variable(2) 

(var1 <-> var2).addDisposableTo(disposeBag) 

var1.value = 10 
print(var2.value) // 10 

var2.value = 20 
print(var1.value) // 20 
+0

Bell'esempio con Variable <-> variabile. Tuttavia non riesco a farlo funzionare con array come Variable <[URL]>. Eventuali suggerimenti? – mkkrolik

Problemi correlati