У меня есть UITextField для поля ИД пользователя в сценарии «Создать учетную запись». Я хочу, чтобы идентификатор пользователя содержал только буквы алфавита c без пробелов.
Я сделал свой контроллер представления UITextFieldDelegate и реализовал функцию shouldChangeCharctersIn (см. Код ниже), чтобы только возвращать верно для буквенно-цифровых символов. Я установил свой контроллер в качестве делегата для текстового поля имени пользователя. Все работает как положено , если не задействовано копирование / вставка или диктовка. В этом случае он почти работает как положено. Если текст, который нужно вставить, содержит какие-либо или не алфавитные символы c, вставка успешно блокируется, за исключением вставки одного пробела.
Немного SO и поиск в Google привел меня к пониманию того, что я нужно отключить умную вставку для UITextField. Я попытался это сделать. Я отключил входную черту SmartInsert (см. Изображение ниже) для этого поля в редакторе раскадровки. Я убедился, что это действительно произошло, проверив свойство smartInsertDeleteType во время viewDidAppear контроллера.
Но ничего не изменилось ...
Я добавил операторы печати в shouldChangeCharctersIn , чтобы я мог видеть, когда он вызывается и что это такое возвращаясь при каждом вызове. Когда диктовка содержит внутренние пробелы (например, «Это тест»), это именно то, что передается в параметре replaceString в shouldChangeCharctersIn . Символ пробела, который был вставлен для отделения этой строки от существующего текста, никогда не проверялся shouldChangeCharctersIn .
Помимо записи строки замены кандидата в консоль, я создал результирующую строку путем вставки строки кандидата в существующий текстовый параметр UITextField. Похоже, что это пробел был добавлен до к вызову shouldChangeCharctersIn , как он появляется в выводе консоли при оценке вставки диктовки (например, «mikemayer67 Это тест»). * Редактировать: я добавил пример вывода консоли в конце этого поста.
Что мне здесь не хватает?
Я не хочу просто выполнять очистку Пробелы перед отправкой формы, так как это может привести к тому, что запутавшемуся пользователю будут нравиться пробелы, введенные этим методом (даже если они не смогут ввести их вручную). Мне также не нравится идея, что нужно всплыть предупреждение, что им нужно исправить проблему, созданную устройством.
Мысли?
extension CreateAccountController : UITextFieldDelegate
{
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
guard let value = textField.text else { return false }
let testString = (value as NSString).replacingCharacters(in: range, with: string)
let rval = validate(textField,string:string)
print("allow: '\(string)' '\(testString)' ", (rval ? "OK" : "NOPE"))
return rval
}
func validate(_ textField: UITextField, string:String) -> Bool
{
var allowedCharacters = CharacterSet.alphanumerics
if textField == password1TextField || textField == password2TextField
{
allowedCharacters.insert(charactersIn: "-!:#$@.")
}
return string.rangeOfCharacter(from: allowedCharacters.inverted) == nil
}
}
allow: 'm' 'm' OK
allow: 'i' 'mi' OK
allow: 'k' 'mik' OK
allow: 'e' 'mike' OK
allow: ' ' 'mike ' NOPE
allow: 'm' 'mikem' OK
allow: 'a' 'mikema' OK
allow: 'y' 'mikemay' OK
allow: 'e' 'mikemaye' OK
allow: 'r' 'mikemayer' OK
allow: 'this is a test ' 'mike this is a test mayer' NOPE
Редактировать: Основываясь на предложении DonMag, я создал следующий подкласс UITextField. Он обрабатывает ввод с клавиатуры, диктовки и копирование / вставку точно так, как мне хотелось бы.
@IBDesignable class LoginTextField: UITextField, UITextFieldDelegate
{
@IBInspectable var allowPasswordCharacters : Bool = false
var validatedText: String?
var dictationText: String?
override init(frame: CGRect)
{
super.init(frame: frame)
delegate = self
}
required init?(coder: NSCoder)
{
super.init(coder: coder)
delegate = self
}
// editing started, so save current text
func textFieldDidBeginEditing(_ textField: UITextField)
{
validatedText = text
dictationText = nil
}
// When dictation ends, the text property will be what we *expect*
// to show up if *shouldChangeCharactersIn* returns true
// Validate the dictated string and either cache it or reset it to
// the last validated text
override func dictationRecordingDidEnd()
{
dictationText = nil
if let t = text
{
let stripped = t.replacingOccurrences(of: " ", with: "")
if validate(string:stripped) {
dictationText = stripped
} else {
dictationText = validatedText
}
}
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
if let t = dictationText
{
// Handle change here, don't let UIKit do it
text = t
validatedText = t
dictationText = nil
}
else if let value = textField.text
{
let testString =
(value as NSString).replacingCharacters(in: range, with: string).replacingOccurrences(of: " ", with: "")
if validate(string:testString)
{
text = testString
validatedText = testString
}
}
return false
}
func validate(string:String) -> Bool
{
var allowedCharacters = CharacterSet.alphanumerics
if allowPasswordCharacters { allowedCharacters.insert(charactersIn: "-!:#$@.") }
return string.rangeOfCharacter(from: allowedCharacters.inverted) == nil
}
}