Как правило, вы не должны изменять статус в shouldChangeCharactersInRange
, даже если вы не используете Rx.Этот обратный вызов - это запрос, а не команда.Текстовое поле просто спрашивает вас, должно ли оно выполнять поведение по умолчанию, а не запрашивает его обновление.Поведение, которое вы пытаетесь реализовать, должно быть в действии editChanged.
Поскольку вы используете Rx, наблюдатель текстового поля rx.text
эквивалентен действию editChanged и должен использоваться вместо него.Самая сложная часть процедуры - убедиться, что вы не потеряете место пользователя, если он вставляет / удаляет в середине строки.
В вашем viewDidLoad:
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)
}
}
}
Кстати, даже если вы представляете клавиатуру с цифровой клавиатурой, вам все равно понадобится такой код, потому что пользователь может подключить клавиатуру Bluetooth и, следовательно, может вводить не числа.