19

I would like to have a UITextField configured with RxSwift/RxCocoa so that it only contains up to ... characters. I do not want to use the UITextFieldDelegate for this but would love to achieve this with RxSwift/RxCocoa. Is there a way to do this?

swalkner
  • 16,679
  • 31
  • 123
  • 210

1 Answers1

39

Sure:

textField.rx.controlEvent(.editingChanged).subscribe(onNext: { [unowned self] in
    if let text = self.textField.text {
        self.textField.text = String(text.prefix(40))
    }
}).disposed(by: disposeBag)

In this example, the textfield is limited to 40 characters.

Edit:

Keeping the previous value when the limit is reached.

textField.rx.text.orEmpty
.scan("") { (previous, new) -> String in
    if new.count > 40 {
        return previous ?? String(new.prefix(40))
    } else {
        return new
    }
}
.subscribe(textField.rx.text)
.disposed(by: disposeBag)

This can probably be adapted to respect other rules...

Please note however that when reaching the character limit, your cursor will jump to the end of the textField.

Valérian
  • 1,068
  • 8
  • 10
  • That's close, but not exactly what I'm looking for. E.g. if the text field has 40 characters and the user types a character at the beginning I would like to prevent that and let the content unchanged, in your case the new character is taken and the last one disappears. – swalkner Feb 14 '18 at 21:42
  • Edited my post to answer your extra criteria. Hope this helps. – Valérian Feb 14 '18 at 22:18
  • @Valérian for your edited answer, the `String(new.prefix(40))` in `return previous ?? String(new.prefix(40))` will never execute, the `previous` value will never be nil so why you are adding it? – mojtaba al moussawi Sep 04 '18 at 08:45
  • Unfortunately, the compiler makes previous optional because `textField.rx.text` expects an optional string. You can get rid of this by adding `.map { Optional($0) }` between `scan` and `subscribe`. – Valérian Sep 04 '18 at 09:28
  • @Valérian `previous` isn't optional and it will never be! What i meant is that there is no meaning for `String(new.prefix(40))` and it will never be executed since previous isn't optional ,You have make it non optional by seeding the `Scan` with empty string `scan("")`... – mojtaba al moussawi Sep 04 '18 at 10:26
  • While I agree with you in theory, using the above code in my project, the compiler considers it as optional when it shouldn't. Have you tried on your side ? – Valérian Sep 04 '18 at 11:44
  • Yes @Valérian , mine it considers it as non-optional. – mojtaba al moussawi Sep 04 '18 at 11:52
  • How to get UITextfield's typing character in Rxswift ? Can anyone help me? – McDonal_11 Nov 11 '19 at 04:14
  • @Valérian For example the textfield is initially blank and then the limit is 2 chars. If programmatically I set the value like this: textfield.text = "123". Textfield will still be blank. How can it get "12" instead? – iadcialim24 Jan 04 '20 at 05:52
  • Setting the text like you mention doesn't trigger a control event so you probably have something else going on. You can add debug operators in the sequence to confirm that. – Valérian Jan 06 '20 at 09:32
  • True. `.scan("") { previous, new in (new.count > 40) ? previous : new }` is enough. – SoftDesigner Dec 30 '20 at 17:16