RxSwift 替换 shouldChangeCharactersInRange

RxSwift replacement shouldChangeCharactersInRange

我想将 UITextfield 与 RxSwift 一起使用。我的目标是 allowing/not 在用户键盘中输入字符并从复制粘贴中删除字符,我需要使用 RxSwift 处理 UITextfield 的委托 "shouldChangeCharactersInRange"。

如何用RxSwift实现?

我正在使用 RxSwift 版本 4。 情况1: 键盘输入:A123 来自 RxSwift 的进程:接受 123(不允许 NumberPad) 输出:123

案例二: 从联系人输入表单复制粘贴:\U202d1111111111\U202c 来自 RxSwift 的过程:删除所有控制字符,接受 1111111111 输出:1111111111

如果一般情况下我们可以使用 shouldChangeCharactersInRange ,但是如何与 RxSwift 一起使用呢?

您可以观察文本更新并在必要时还原它:

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

一般来说,您不应该在 shouldChangeCharactersInRange 中改变状态,即使您没有使用 Rx。该回调是查询而不是命令。文本字段只是询问您是否应该执行默认行为,而不是告诉您更新它。您尝试实现的行为应该在 editingChanged 操作中。

由于您使用的是 Rx,因此文本字段的 rx.text 观察器等同于 editingChanged 操作,应该改为使用。该过程中最困难的部分是如果用户 inserting/deleting 在字符串中间,请确保您不会丢失用户的位置。

在您看来DidLoad:

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

支持全局函数:

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)
        }
    }
}

顺便说一句,即使您使用的是数字键盘,您仍然需要这样的代码,因为用户可能连接了蓝牙键盘,因此仍然可以输入非数字。