RxSwift replacement shouldChangeCharactersInRange

…衆ロ難τιáo~ 提交于 2019-12-04 13:20:33

问题


I want use UITextfield with RxSwift. My goal is allowing/not input character in User keyboard and removing character from copy paste, I need handle UITextfield's delegate "shouldChangeCharactersInRange" with RxSwift.

How to implement with RxSwift?

I am using RxSwift version 4. Case 1: Input from keyboard: A123 Process from RxSwift : Accept 123 (not allowing NumberPad) Output : 123

Case 2: Input form Copy Paste from Contacts: \U202d1111111111\U202c Process from RxSwift : remove all control character, accept 1111111111 Output: 1111111111

If in general we can use shouldChangeCharactersInRange , but how to use with RxSwift?


回答1:


In general, you should not be mutating state in shouldChangeCharactersInRange, even if you aren't using Rx. That callback is a query not a command. The textfield is merely asking you if it should perform the default behavior, not telling you to update it. The behavior you are trying to implement should be in the editingChanged action.

Since you are using Rx, the text field's rx.text observer is equivalent to the editingChanged action and should be used instead. The hardest part of the procedure is making sure you don't loose the user's place if they are inserting/deleting in the middle of the string.

In your viewDidLoad:

textField.rx.text.orEmpty
    .map(digitsOnly)
    .subscribe(onNext: setPreservingCursor(on: textField))
    .disposed(by: bag)

Supporting global functions:

func digitsOnly(_ text: String) -> String {
    return text.components(separatedBy: CharacterSet.decimalDigits.inverted).joined(separator: "")
}

func setPreservingCursor(on textField: UITextField) -> (_ newText: String) -> Void {
    return { newText in
        let cursorPosition = textField.offset(from: textField.beginningOfDocument, to: textField.selectedTextRange!.start) + newText.count - (textField.text?.count ?? 0)
        textField.text = newText
        if let newPosition = textField.position(from: textField.beginningOfDocument, offset: cursorPosition) {
            textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
        }
    }
}

BTW, even if you are presenting the number pad keyboard, you still need some code like this because the user might have a bluetooth keyboard hooked up and thus could still enter non-numbers.




回答2:


You can observe text update and revert it when necessary:

Observable.zip(textfield.rx.text, textfield.rx.text.skip(1))
    .subscribe(onNext: { (old, new) in
        if $invalid {
            textfield.text = old
        }
    })


来源:https://stackoverflow.com/questions/50558613/rxswift-replacement-shouldchangecharactersinrange

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!