Встановіть максимальну довжину символу поля UITextField у Swift


85

Я знаю, що на це є інші теми, але, здається, я не можу з’ясувати, як це реалізувати.

Я намагаюся обмежити 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


Swift 4 реалізація stackoverflow.com/a/46513151/2303865
Лео Dabus

Швидке оповіщення - щоб скоротитиString в Swift в ці дні ви можете , нарешті , просто .prefix (п)
Fattie

Відповіді:


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

Для Свіфта 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
Аладін

Але куди б я поставив другий параметр? Я більше не посилаюся на myTextField після того, як встановив його як делегат.
ishkur88

Як якщо б я хотів зробити інше текстове поле 0-9 лише для телефонних номерів.
ishkur88

кожного разу, коли редагується текстове поле, викликається зворотний виклик shouldChangeCharactersInRange, це стосується всіх текстових полів, ви отримуєте зворотний дзвінок в одному shouldChangeCharactersInRangeі тому ж місці, і всередині цього методу ви можете знати, яке текстове поле редагується завдяки переданому параметру, textFieldви можете, наприклад, дати тег для кожного текстового поля і тест всередині shouldChangeCharactersInRangeі для кожного текстового поля виконують перевірку вмісту
Аладін

113

Грудень 2017. Свіфт 4.

Будьте уважні, що значна частина прикладу коду, який ви побачите в Інтернеті щодо цієї проблеми, дуже застаріла .

Вставте наступне в будь-який файл 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 )


Для програміста OCD (як я) ...

Як нагадує @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

2
чи є щось подібне для textView?
Дорі

13
Що все це потрібно зробити, щоб досягти чогось такого загального і такого простого, мене вражає. Вони не могли просто дати нам простий вбудований textField.maxLength ... у будь-якому випадку ваше рішення чудове, дякую!
mylovemhz

2
Рішення зручне, але карта текстових полів у верхній частині насправді створить цикл збереження.
Angel G. Olloqui,

3
ПОПЕРЕДЖЕННЯ ТИМ, ЩО ВИКОРИСТОВУЮТЬ ЦЕ РІШЕННЯ! Це рішення має деякі проблеми. Один з них полягає в тому, що якщо користувач набирає текст на початку textField, ви дозволите йому ввести новий символ, і останній буде видалено, а також курсор перейде до останнього символу в полі. Інша проблема полягає в тому, що якщо ви встановите текст програмно, це дозволить вам встановити текст, більший за граничний. Інша проблема виникає, якщо ви скасуєте зміну (CMD + Z із зовнішньою клавіатурою), вона вийде з ладу, якщо ви спробували додати цифру за ліміт раніше.
juancazalla

26

Свіфт 4, просто використовуйте:

public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    return range.location < 10
}

це має бути обрана відповідь. Простий і працює добре.
user832

9
Це не працює. Що робити, якщо натиснути посередині рядка, і ви можете ввести більше X символів.
Славчо

замість range.location <10, ви можете використовувати textField.text.length <10. Це рішення просте та елегантне.
Saqib Сауд

Ви можете використовувати це рішення: 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
Павлос,


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властивість має бути дозвільним, якщо воно не встановлене замість штучних 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, будь ласка?
Кіхан Камангар,

3

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

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
Ankur Lahiry

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


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

оновлення для цієї відповіді Fattie

Дякую

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

        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, і вона досить пряма вперед із можливістю пропускати зворотний простір.

func textField(_ textField: UITextField, 
               shouldChangeCharactersIn range: NSRange, 
               replacementString string: String) -> Bool {
    return textField.text!.count < 10 || string == ""
}

Це не обробляє копіювання та вставлення
cornr

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
}

Примітка: Тут я перевірив наявність лише символів, дозволених у textField


1

Символ обмеження TextField після блокування тексту в 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

Я використовую цей крок, спочатку встановіть делегатське текстове поле у ​​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)
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.