Markus Rautopuro
Markus Rautopuro

Reputation: 8365

How do you get a signal every time a UITextField text property changes in ReactiveCocoa 5

How do you get a signal from both user-initiated and programmatically made changes to UITextField text property? By using continuousTextValues only reports a signal when the user has initiated the change. If you set textField.text programmatically, the signal doesn't fire.

This is how I'm using continuousTextValues:

textField.reactive.continuousTextValues.observeValues { value in
    print("Value: \(value)")
}

It doesn't get triggered if I set text manually:

textField.text = "Test"

Upvotes: 10

Views: 4299

Answers (4)

ixx
ixx

Reputation: 32273

After setting the text, trigger the editing event: textField.sendActions(for: .editingChanged)

Upvotes: 0

jlukanta
jlukanta

Reputation: 169

To listen to programmatic changes in text property:

let textField = UITextField()
let signal = textField.reactive.signal(forKeyPath: #keyPath(UITextField.text)).map { $0 as? String }
signal.observeValues { print($0) }
textField.text = "Hello World"

Note that you still need to observe continuousTextValues if you want to observe changes due to user actions.

Upvotes: 0

Benjamin Cheah
Benjamin Cheah

Reputation: 1401

Instead of changing the text value with textField.text, you have to create a signal, bind it to the text field and modify the signal's value. I've used a Signal pipe but if you need to store the programmatically changed value, you can use a MutableProperty.

class MyViewController: UIViewController {
    var textField: UITextField!
    private let textFieldValuePipe = Signal<String?, NoError>.pipe()
    var textFieldValueSignal: Signal<String?, NoError>!

    override func viewDidLoad() {
        // Initialize the text field
        // ...

        // Bind the text of the text field to the signal pipe's output
        textField.reactive.text <~ textFieldValuePipe.output

        // A signal of text values emitted by the text field upon end of editing.
        let textFieldValuesSignal = textField.reactive.textValues

        // A signal of text values emitted by the text field upon any changes.
        let textFieldContinuousValuesSignal = textField.reactive.continuousTextValues

        // Merge the relevant signals
        textFieldValueSignal = Signal.merge(textFieldValuesSignal, textFieldContinuousValuesSignal, textFieldValuePipe.output)

        // This will print the text field's value when it's changed by the user or programmatically
        textFieldValueSignal.observeValues { value in
            print(value ?? "nil")
        }
    }

    // Use this to change the text field's value programmatically
    func setTextFieldText(_ text: String?) {
        textFieldValuePipe.input.send(value: text)
    }

}

Upvotes: 1

Shaw
Shaw

Reputation: 92

The signal continuousTextValueswill only be triggered while user input using the keyboard.You could try this:

var characters = MutableProperty("")

tf.reactive.text <~ characters
tf.reactive.continuousTextValues.observeValues { [weak characters = characters] (text) in
   characters?.value = text!
}
tf.reactive.textValues.observeValues { [weak characters = characters] (text) in
   characters?.value = text!
}

characters.producer.skip(while: { $0.isEmpty }).startWithValues { (text) in
   log.debug("text = \(text)")
}

characters.value = "shaw"

Upvotes: 5

Related Questions