RxSwift replacement shouldChangeCharactersInRange

前端 未结 2 668
小鲜肉
小鲜肉 2020-12-16 03:04

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

相关标签:
2条回答
  • 2020-12-16 03:42

    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.

    0 讨论(0)
  • 2020-12-16 03:47

    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
            }
        })
    
    0 讨论(0)
提交回复
热议问题