RxSwift/RxCocoa: prevent UITextField from having more than ... characters
Asked Answered
P

2

20

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?

Palter answered 14/2, 2018 at 15:12 Comment(0)
B
40

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.

Begone answered 14/2, 2018 at 21:26 Comment(11)
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.Palter
Edited my post to answer your extra criteria. Hope this helps.Autotrophic
@Begone 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?Floats
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.Autotrophic
@Begone 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("")...Floats
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 ?Autotrophic
Yes @Begone , mine it considers it as non-optional.Floats
How to get UITextfield's typing character in Rxswift ? Can anyone help me?Economist
@Begone 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?Evania
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.Autotrophic
True. .scan("") { previous, new in (new.count > 40) ? previous : new } is enough.Najera
C
2

Example with 10 characters limit:

textField.rxText.orEmpty.map { String($0.prefix(10)) }
        .bind(to: textField.rxText)
        .disposed(by: disposeBag)
Catheryncatheter answered 18/10, 2023 at 12:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.