简体   繁体   中英

Swift 2 RAC4 Merge UITextField Signal and UISwitch Signal

I've written a reactive-imperative version of the code:

widthTextField.rac_textSignal().subscribeNext { (val) in
    let multiplier = 0.2
    if var v = Double(val as! String){
        if(self.viewModel.proportionalBool.value){
            v *= multiplier
            self.heightTextField.text = String(v)
        }
    }
}

What I have in my ViewModel are three MutableProperties: proportionalBool of type Bool, heightString and widthString of type String.

I also have a corresponding UISwitch and two UITextFields in my ViewController.

What I wish to learn is having the following scenario: 1. If bool/switch is false, then heightString should be untouched 2. If bool is true, then heightString should be a value of: widthString * multiplier

I know I've written the code in a bad way. I think I should have used combineLatest but I have no idea how to merge two signals: UITextField and UISwitch into one producing signal.

Could anyone give me any hint or a simple code example?

let boolSignal = proportionalSwitch.rac_newOnChannel()
let widthSignal = widthTextField.rac_textSignal()
let heightSignal = heightTextField.rac_textSignal()
...?

I'm going to assume that you want always want to have the MutableProperties in your ViewModel always have the correct values.

Also I'm going to use REX ( which will soon be a part of RAC anyway ) for simpler UI Bindings.

Lets say this is your viewModel:

final class ViewModel {
    let on = MutableProperty<Bool>(false)
    let width = MutableProperty<Double>(0.0)
    let height = MutableProperty<Double>(0.0)
}

The first part is binding the switch and the textfields to the MutableProperties of your viewModel.

The first two are relatively straight forward:

viewModel.on <~ onSwitch.rex_on
viewModel.width <~ widthTextField.rex_textSignal
    .map { Double($0) ?? 0 }

Note: I've chosen to send 0 as width if the input of the width textfield is not a valid number. Depending on your needs, you might want to handle this case differently, eg by having the width and height properties have optional values...

Now the height is almost the same, but we have to make sure its only processed while the switch is on . This is achieved by using combineLatest and filter :

viewModel.height <~ combineLatest(viewModel.on.producer, viewModel.width.producer)  // SignalProducer<(Bool, Double), NoError>
    .filter { on, text in return on }   // Only send values when the switch is `on`
    .map { on, width in return width }  // Now we dont care about the switch value anymore, extract just the width
    .map { width in return 0.5 * width }// Transform the width as you wish

Now, all thats left is to bind the value of the height to the UI. Unless I'm missing something, theres no help from REX for that (at least not for UITextField ), so we'll have to use a DynamicProperty :

override func viewDidLoad() {
    super.viewDidLoad()

    // Previously mentioned binding to the viewModel

    DynamicProperty(object: heightTextField, keyPath: "text") <~ viewModel.height.producer.map { String($0) }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM