Установите максимальную длину символа UITextField в Swift

88

Я знаю, что есть другие темы по этому поводу, но я не могу понять, как это реализовать.

Я пытаюсь ограничить UITextField только 5 символами

Желательно буквенно-цифровые и - и. и _

Я видел этот код

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange,
                       replacementString string: String) -> Bool
{
    let maxLength = 4
    let currentString: NSString = textField.text
    let newString: NSString =
             currentString.stringByReplacingCharactersInRange(range, withString: string)
    return newString.length <= maxLength
}

и

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

    let length = count(textField.text.utf16) + count(string.utf16) - range.length
    return length <= 10 
}

Я просто не знаю, как на самом деле это реализовать или какое «текстовое поле» мне следует заменить на свое пользовательское с именем UITextField.

ishkur88
источник
Реализация Swift 4 stackoverflow.com/a/46513151/2303865
Лео Дабус
Быстрое оповещение - чтобы сократитьString в Swift в эти дни вы можете , наконец , просто .prefix (п)
Fattie

Ответы:

136
  1. Ваш контроллер представления должен соответствовать UITextFieldDelegate, как показано ниже:

    class MyViewController: UIViewController, UITextFieldDelegate {
    
    }
    
  2. Установите делегата вашего текстового поля: myTextField.delegate = self

  3. Реализуйте метод в вашем контроллере представления: textField(_:shouldChangeCharactersInRange:replacementString:)

Все вместе:

class MyViewController: UIViewController,UITextFieldDelegate  //set delegate to class 

@IBOutlet var mytextField: UITextField             //  textfield variable 

override func viewDidLoad() {
    super.viewDidLoad()
    mytextField.delegate = self                  //set delegate
}


func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange,
                       replacementString string: String) -> Bool
{
    let maxLength = 4
    let currentString: NSString = textField.text
    let newString: NSString =
             currentString.stringByReplacingCharactersInRange(range, withString: string)
    return newString.length <= maxLength
}

Для Swift 4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let maxLength = 1
    let currentString: NSString = (textField.text ?? "") as NSString
    let newString: NSString =
        currentString.replacingCharacters(in: range, with: string) as NSString
    return newString.length <= maxLength
}

Разрешить ввод только указанного набора символов в данное текстовое поле

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
  var result = true
  
  if mytextField == numberField {
    if count(string) > 0 {
      let disallowedCharacterSet = NSCharacterSet(charactersInString: "0123456789.-").invertedSet
      let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
      result = replacementStringIsLegal
    }
  }
 
  return result
}

Как запрограммировать текстовое поле iOS, которое принимает только числовой ввод с максимальной длиной

Аладин
источник
Большое спасибо за оперативный ответ! Если я установлю это одно текстовое поле в качестве делегата, смогу ли я изменить другие текстовые поля?
ishkur88
да, и вы получите рассматриваемое текстовое поле (редактируемое) в качестве первого параметра textFieldв методе func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
Aladin
Но где бы я поместил второй параметр? Я больше не ссылаюсь на myTextField после того, как установил его как делегата.
ishkur88
Например, если бы я хотел сделать еще одно текстовое поле 0-9 только для телефонных номеров.
ishkur88
каждый раз, когда текстовое поле редактируется, shouldChangeCharactersInRangeвызывается обратный вызов , это для всех текстовых полей, вы получаете обратный вызов в одном shouldChangeCharactersInRangeи том же месте, и внутри этого метода вы можете узнать, какое текстовое поле редактируется благодаря переданному параметру, который textFieldвы можете, например, дать тег для каждого текстового поля и тест внутри shouldChangeCharactersInRangeи для каждого текстового поля выполняет проверку содержимого
Aladin
117

Современный Свифт

Обратите внимание, что большая часть кода онлайн, посвященного этой проблеме, чрезвычайно устарела .

Вставьте следующее в любой файл Swift в своем проекте. (Вы можете назвать файл как угодно, например "Handy.swift".)

Это, наконец, решает одну из самых глупых проблем iOS:

введите описание изображения здесь

Теперь ваши текстовые поля имеют расширение .maxLength.

Вполне нормально установить это значение в раскадровке во время разработки или установить его в коде во время работы приложения.

// simply have this in any Swift file, say, Handy.swift

import UIKit
private var __maxLengths = [UITextField: Int]()
extension UITextField {
    @IBInspectable var maxLength: Int {
        get {
            guard let l = __maxLengths[self] else {
               return 150 // (global default-limit. or just, Int.max)
            }
            return l
        }
        set {
            __maxLengths[self] = newValue
            addTarget(self, action: #selector(fix), for: .editingChanged)
        }
    }
    func fix(textField: UITextField) {
        let t = textField.text
        textField.text = t?.prefix(maxLength)
    }
}

Это так просто.


Сноска - в наши дни, чтобы безопасно обрезать Stringбыстро, вы просто.prefix(n)


Еще более простая разовая версия ...

Это исправляет все текстовые поля в вашем проекте.

Если вы просто хотите, чтобы в одном конкретном текстовом поле было просто написано «4», и все ...

class PinCodeEntry: UITextField {
    
    override func didMoveToSuperview() {
        
        super.didMoveToSuperview()
        addTarget(self, action: #selector(fixMe), for: .editingChanged)
    }
    
    @objc private func fixMe() { text = text?.prefix(4) }
}

Фух! Это все, что нужно сделать.

(Просто BTW, вот подобный очень полезный совет , касающиеся UIText View , https://stackoverflow.com/a/42333832/294884 )


Для программиста ОКР (вроде меня) ...

Как напоминает @LeoDabus, .prefixвозвращает подстроку. Если вы невероятно заботливы, это

let t = textField.text
textField.text = t?.prefix(maxLength)

было бы

if let t: String = textField.text {
    textField.text = String(t.prefix(maxLength))
}

Наслаждайтесь!

Толстяк
источник
2
Выглядит вполне законно! Спасибо.
J. Doe
13
То, что все это должно быть сделано для достижения чего-то столь обычного и столь простого, поражает меня. Они не могли просто дать нам простой встроенный textField.maxLength ... в любом случае ваше решение отличное, спасибо!
mylovemhz
1
Вау, лучший совет, который я когда-либо получал от SO. Я бы проголосовал за 100, если бы мог!
jonathan3087
2
Решение удобное, но карта текстовых полей вверху фактически создает цикл сохранения.
Анхель Г. Оллоки
3
ПРЕДУПРЕЖДЕНИЕ ДЛЯ ПОЛЬЗОВАТЕЛЕЙ ЭТОГО РЕШЕНИЯ! У этого решения есть некоторые проблемы. Один из них заключается в том, что если пользователь вводит текст в начале текстового поля, вы разрешаете им вводить новый символ, а последний будет удален, а также курсор переместится к последнему символу в поле. Другая проблема заключается в том, что если вы установите текст программно, это позволит вам установить текст, превышающий лимит. Другая проблема возникает, если вы отмените изменение (CMD + Z с внешней клавиатурой), оно выйдет из строя, если вы ранее пытались добавить цифру сверх лимита.
juancazalla
25

Swift 4, просто используйте:

public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    return range.location < 10
}
Сергей Билык
источник
это должен быть выбранный ответ. Просто и хорошо работает.
user832
9
Это не работает. Что делать, если вы нажмете на середину строки и сможете ввести больше X символов.
Slavcho
вместо range.location <10 вы можете использовать textField.text.length <10. Это простое и элегантное решение.
Сакиб Сауд
Вы можете использовать это решение: if textField.text? .Count> = 12 {return false}
Сергей Билык
1
он не работает, когда мимо текста, если вы хотите работать в прошлом действии, которое вы должны добавитьstring.count < MAX_LENGTH
Реза Дехнави,
12

Так же, как это сделал Стивен Шматц, но с использованием Swift 3.0:

//max Length
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
               replacementString string: String) -> Bool
{
    let maxLength = 4
    let currentString: NSString = textField.text! as NSString
    let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString
    return newString.length <= maxLength
}
Павлос
источник
1
Хороший ответ. Оценил.
Хася
Спасибо @Hasya
Pavlos
9

Для Swift 5:
просто напишите одну строку, чтобы установить максимальную длину символа:

 self.textField.maxLength = 10

Больше подробностей нажмите здесь

Кредит: http://www.swiftdevcenter.com/max-character-limit-of-uitextfield-and-allowed-characters-swift/

Ашиш Чаухан
источник
Хотя это решение может показаться простым, на самом деле для его реализации требуется гораздо больше кода. Этот ответ сам по себе не очень полезен, и добавление объяснения и включение других соответствующих фрагментов кода может быть полезным.
FontFamily
6

Думаю, для этого удобнее расширение. Смотрите полный ответ здесь

private var maxLengths = [UITextField: Int]()

// 2
extension UITextField {

  // 3
  @IBInspectable var maxLength: Int {
    get {
      // 4
      guard let length = maxLengths[self] else {
        return Int.max
      }
      return length
    }
    set {
      maxLengths[self] = newValue
      // 5
      addTarget(
        self,
        action: #selector(limitLength),
        forControlEvents: UIControlEvents.EditingChanged
      )
    }
  }

  func limitLength(textField: UITextField) {
    // 6
    guard let prospectiveText = textField.text
      where prospectiveText.characters.count > maxLength else {
        return
    }

    let selection = selectedTextRange
    // 7
    text = prospectiveText.substringWithRange(
      Range<String.Index>(prospectiveText.startIndex ..< prospectiveText.startIndex.advancedBy(maxLength))
    )
    selectedTextRange = selection
  }

}
ЗаЕМ ЗаФар
источник
4

Другие решения, опубликованные выше, создают цикл сохранения из-за карты текстового поля. Кроме того, maxLengthсвойство должно допускать значение NULL, если не установлено вместо искусственных Int.maxконструкций; и цель будет установлена ​​несколько раз, если maxLength изменится.

Здесь обновленное решение для Swift4 со слабой картой для предотвращения утечек памяти и другими исправлениями.

private var maxLengths = NSMapTable<UITextField, NSNumber>(keyOptions: NSPointerFunctions.Options.weakMemory, valueOptions: NSPointerFunctions.Options.strongMemory)

extension UITextField {

    var maxLength: Int? {
        get {
            return maxLengths.object(forKey: self)?.intValue
        }
        set {
            removeTarget(self, action: #selector(limitLength), for: .editingChanged)
            if let newValue = newValue {
                maxLengths.setObject(NSNumber(value: newValue), forKey: self)
                addTarget(self, action: #selector(limitLength), for: .editingChanged)
            } else {
                maxLengths.removeObject(forKey: self)
            }
        }
    }

    @IBInspectable var maxLengthInspectable: Int {
        get {
            return maxLength ?? Int.max
        }
        set {
            maxLength = newValue
        }
    }

    @objc private func limitLength(_ textField: UITextField) {
        guard let maxLength = maxLength, let prospectiveText = textField.text, prospectiveText.count > maxLength else {
            return
        }
        let selection = selectedTextRange
        text = String(prospectiveText[..<prospectiveText.index(from: maxLength)])
        selectedTextRange = selection
    }
}
Анхель Дж. Оллоки
источник
Спасибо за ответ. Не могли бы вы объяснить maxLengths?
Кейхан Камангар
4

Простое решение без использования делегата:

TEXT_FIELD.addTarget(self, action: #selector(editingChanged(sender:)), for: .editingChanged)


@objc private func editingChanged(sender: UITextField) {

        if let text = sender.text, text.count >= MAX_LENGHT {
            sender.text = String(text.dropLast(text.count - MAX_LENGHT))
            return
        }
}
обер
источник
2
Ответ, который я ищу: D
Анкур Лахири
1
Это решение, простое и элегантное, без шаблонного кода.
zeeshan
3

Моя версия Swift 4 shouldChangeCharactersIn

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

        guard let preText = textField.text as NSString?,
            preText.replacingCharacters(in: range, with: string).count <= MAX_TEXT_LENGTH else {
            return false
        }

        return true
    }
авиран
источник
Это должен быть принятый ответ. Работает отлично, без ошибок.
10623169
2

Мне есть что добавить к ответу Аладина:

  1. Ваш контроллер представления должен соответствовать UITextFieldDelegate

    class MyViewController: UIViewController, UITextViewDelegate {
    
    }
    
  2. Задайте делегата вашего текстового поля: чтобы установить делегата, вы можете управлять перетаскиванием из текстового поля в контроллер представления в раскадровке. Я думаю, что это предпочтительнее, чем устанавливать его в коде

  3. Реализуйте метод в вашем контроллере представления: textField(_:shouldChangeCharactersInRange:replacementString:)

SwiftMatt
источник
2

Даю дополнительный ответ на основе @Frouo. Я думаю, что его ответ - самый красивый. Потому что это обычный элемент управления, который мы можем использовать повторно. И здесь нет проблемы с утечкой.

    private var kAssociationKeyMaxLength: Int = 0

    extension UITextField {

        @IBInspectable var maxLength: Int {
            get {
                if let length = objc_getAssociatedObject(self, &kAssociationKeyMaxLength) as? Int {
                    return length
                } else {
                    return Int.max
                }
            }
            set {
                objc_setAssociatedObject(self, &kAssociationKeyMaxLength, newValue, .OBJC_ASSOCIATION_RETAIN)
                self.addTarget(self, action: #selector(checkMaxLength), for: .editingChanged)
            }
        }

//The method is used to cancel the check when use Chinese Pinyin input method.
        //Becuase the alphabet also appears in the textfield when inputting, we should cancel the check.
        func isInputMethod() -> Bool {
            if let positionRange = self.markedTextRange {
                if let _ = self.position(from: positionRange.start, offset: 0) {
                    return true
                }
            }
            return false
        }


        func checkMaxLength(textField: UITextField) {

            guard !self.isInputMethod(), let prospectiveText = self.text,
                prospectiveText.count > maxLength
                else {
                    return
            }

            let selection = selectedTextRange
            let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
            text = prospectiveText.substring(to: maxCharIndex)
            selectedTextRange = selection
        }



    }
Виктор Чой
источник
2

обновление для этого толстого ответа

благодаря

extension UITextField {

    /// Runtime key
    private struct AssociatedKeys {
        /// max lenght key
        static var maxlength: UInt8 = 0
        /// temp string key
        static var tempString: UInt8 = 0
    }

    /// Limit the maximum input length of the textfiled
    @IBInspectable var maxLength: Int {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.maxlength) as? Int ?? 0
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.maxlength, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            addTarget(self, action: #selector(handleEditingChanged(textField:)), for: .editingChanged)
        }
    }

    /// temp string
    private var tempString: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.tempString) as? String
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.tempString, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    /// When the text changes, process the amount of text in the input box so that its length is within the controllable range.
    @objc private func handleEditingChanged(textField: UITextField) {

        /// Special Processing for Chinese Input Method
        guard markedTextRange == nil else { return }

        if textField.text?.count == maxLength {

            /// SET lastQualifiedString where text length == max lenght
            tempString = textField.text
        } else if textField.text?.count ?? 0 < maxLength {

            /// clear lastQualifiedString when text lengeht > maxlength
            tempString = nil
        }

        /// keep current text range in arcgives
        let archivesEditRange: UITextRange?

        if textField.text?.count ?? 0 > maxLength {

            /// if text length > maxlength,remove last range,to move to -1 postion.
            let position = textField.position(from: safeTextPosition(selectedTextRange?.start), offset: -1) ?? textField.endOfDocument
            archivesEditRange = textField.textRange(from: safeTextPosition(position), to: safeTextPosition(position))
        } else {

            /// just set current select text range
            archivesEditRange = selectedTextRange
        }

        /// main handle string max length
        textField.text = tempString ?? String((textField.text ?? "").prefix(maxLength))

        /// last config edit text range
        textField.selectedTextRange = archivesEditRange
    }

    /// get safe textPosition
    private func safeTextPosition(_ optionlTextPosition: UITextPosition?) -> UITextPosition {

        /* beginningOfDocument -> The end of the the text document. */
        return optionlTextPosition ?? endOfDocument
    }
}
Чжэн
источник
1

Работа в Swift4

// ШАГ 1 устанавливает UITextFieldDelegate

    class SignUPViewController: UIViewController , UITextFieldDelegate {

       @IBOutlet weak var userMobileNoTextFiled: UITextField!

        override func viewDidLoad() {
            super.viewDidLoad()

// ШАГ 2 установка делегата
userMobileNoTextFiled.delegate = self // установка делегата}

     func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    //        guard let text = userMobileNoTextFiled.text else { return true }
    //        let newLength = text.count + string.count - range.length
    //        return newLength <= 10
    //    }

// ШАГ 3 вызывает функцию

        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            let maxLength = 10          // set your need
            let currentString: NSString = textField.text! as NSString
            let newString: NSString =
                currentString.replacingCharacters(in: range, with: string) as NSString
            return newString.length <= maxLength
        }
    }
Кешав Гера
источник
1

Это ответ для Swift 4, и он довольно прост с возможностью пропускать backspace.

func textField(_ textField: UITextField, 
               shouldChangeCharactersIn range: NSRange, 
               replacementString string: String) -> Bool {
    return textField.text!.count < 10 || string == ""
}
CodeBender
источник
Это не обрабатывает копирование и вставку
Корнр
1

Просто проверьте количество символов в строке

  1. Добавить делегата для просмотра контроллера и назначенного делегата
    class YorsClassName : UITextFieldDelegate {

    }
  1. проверьте количество символов, разрешенных для текстового поля
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if textField.text?.count == 1 {
        return false
    }
    return true
}

Примечание: здесь я проверил только char, разрешенный в textField

Акшай Диграз
источник
1

Символ ограничения текстового поля после блока текста в Swift 4

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


    if textField == self.txtDescription {
        let maxLength = 200
        let currentString: NSString = textField.text! as NSString
        let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString
        return newString.length <= maxLength
    }

    return true


}
Правин Редди
источник
1

На всякий случай не забывайте охранять размер диапазона перед его применением к строке. В противном случае произойдет сбой, если пользователь сделает следующее:

Введите текст максимальной длины Вставьте что-нибудь (ничего не будет вставлено из-за ограничения длины, но iOS не знает об этом) Отменить вставку (происходит сбой, диапазон причин будет больше фактического размера строки)

Также при использовании iOS 13 пользователи могут случайно вызвать это жестами.

Предлагаю вам добавить в свой проект это

extension String {
    func replace(with text: String, in range: NSRange) -> String? {
        guard range.location + range.length <= self.count else { return nil }
        return (self as NSString).replacingCharacters(in: range, with: text)
    }
}

И используйте это так:

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    guard let newText = textView.text.replace(with: text, in: range) else { return false }
    return newText.count < maxNumberOfCharacters
}

В противном случае ваше приложение будет постоянно падать.

кикивора
источник
0

Вот альтернатива Swift 3.2+, которая позволяет избежать ненужных манипуляций со строками. В этом случае максимальная длина 10:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let text = textField.text ?? ""

    return text.count - range.length + string.count <= 10
}
Грег Браун
источник
0

Я использую этот шаг, сначала установите texfield делегата в viewdidload.

    override func viewDidLoad() {
        super.viewDidLoad()

        textfield.delegate = self

    }

а затем shouldChangeCharactersIn после включения UITextFieldDelegate.

extension viewController: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
                let newLength = (textField.text?.utf16.count)! + string.utf16.count - range.length
                if newLength <= 8 { 
                    return true
                } else {
                    return false
                }
            }
    }
Джери ПМ
источник
0

Если у вас есть несколько текстовых полей с проверками разной длины на одной странице, я нашел простое и короткое решение.

class MultipleTextField: UIViewController {

    let MAX_LENGTH_TEXTFIELD_A = 10
    let MAX_LENGTH_TEXTFIELD_B = 11

    lazy var textFieldA: UITextField = {
        let textField = UITextField()
        textField.tag = MAX_LENGTH_TEXTFIELD_A
        textField.delegate = self
        return textField
    }()
    lazy var textFieldB: UITextField = {
        let textField = UITextField()
        textField.tag = MAX_LENGTH_TEXTFIELD_B
        textField.delegate = self
        return textField
    }()
}

extension MultipleTextField: UITextFieldDelegate {
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        return (range.location < textField.tag) && (string.count < textField.tag)
    }
}
Реза Дехнави
источник