Как избежать нежелательных лишних пробелов, вставляемых диктовкой в ​​UITextField - PullRequest
0 голосов
/ 06 марта 2020

У меня есть 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
  }
}

username UITextField input settings

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

1 Ответ

1 голос
/ 07 марта 2020

Работа с диктовкой может быть сложной.

Я не раз сжигал эту вставку лишнего пробела - и это только когда я использую диктовку в других приложениях ... даже не говоря о написании кода для него.

Это может сработать для вас, хотя вы можете внести некоторые изменения, чтобы улучшить его. Например, после того, как пользователь заканчивает диктовку, точка вставки перемещается в конец строки.

Я вложил в подкласс UITextField и реализовал всю проверку и обработку делегатов внутри класса. Вы можете попробовать его, просто добавив новый UITextField и присвоив его пользовательскому классу MyTextField:

class MyTextField: UITextField, UITextFieldDelegate {

    var myCurText: String?
    var myNewText: String?

    var isDictation: Bool = false

    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) {
        // unwrap the text
        if let t = text {
            myCurText = t
        }
    }

    // when dictation ends, the text will be what we *expect*
    //  e.g.
    //      text is "ABCD"
    //      insertion point is between the B and C
    //      user dictates "Test"
    //      text is now "ABTestCD"
    //  or
    //      user dictates "This is a test"
    //      text is now "ABThis is a testCD"
    //
    // So, we can validate the string and set a flag telling
    //  shouldChangeCharactersIn range not to do normal processing
    override func dictationRecordingDidEnd() {
        // set flag that we just dictated something
        isDictation = true

        // unwrap the text
        if let t = text {
            // just for debuggging
            print("Dictation Ended: [\(t)]")
            // strip spaces from the whole string
            let stripped = t.replacingOccurrences(of: " ", with: "")
            // validate the stripped string
            if validate(self, string: stripped) {
                // just for debugging
                print("Valid! setting text to:", stripped)
                // it's a valid string, so update myNewText
                myNewText = stripped
            } else {
                // just for debugging
                print("NOT setting text to:", stripped)
                // it's NOT a valid string, so set myNewText to myCurText
                myNewText = myCurText
            }
        }
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

        // if we just received a dictation
        if isDictation {
            // update self.text
            text = myNewText
            // update myCurText variable
            myCurText = myNewText
            // turn off the dictation flag
            isDictation = false
            // returning false from shouldChangeCharactersIn
            return false
        }

        // we get here if it was NOT a result of dictation

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

        if rval {
            // if valid string, update myCurText variable
            myCurText = testString
        }
        return rval

    }

    func validate(_ textField: UITextField, string:String) -> Bool
    {
        var allowedCharacters = CharacterSet.alphanumerics
        allowedCharacters.insert(charactersIn: "-!:#$@.")
        return string.rangeOfCharacter(from: allowedCharacters.inverted) == nil
    }

}

Если он не совсем справляется с работой, вы можете прочитать его Документы Apple для UITextInput -> Использование диктовки

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...