web-dev-qa-db-fra.com

Définir la longueur maximale de caractères d'un UITextField dans Swift

Je sais qu'il y a d'autres sujets à ce sujet mais je n'arrive pas à trouver comment le mettre en œuvre. 

J'essaie de limiter un UITextField à 5 caractères. 

De préférence alphanumérique et - et. et _ 

J'ai vu ce code 

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
}

et 

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 
}

Je ne sais tout simplement pas comment le mettre en œuvre ni quel "champ de texte" dois-je échanger contre mon programme personnalisé nommé UITextField

43
ishkur88
  1. Votre contrôleur de vue doit être conforme à UITextFieldDelegate, comme ci-dessous:

    class MyViewController: UIViewController, UITextFieldDelegate {
    
    }
    
  2. Définissez le délégué de votre champ de texte: myTextField.delegate = self

  3. Implémentez la méthode dans votre contrôleur de vue: textField(_:shouldChangeCharactersInRange:replacementString:)

Tous ensemble:

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
}

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

Ne permettre la saisie que d'un ensemble de caractères spécifié dans un champ de texte donné

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
}

Comment programmer un champ de texte iOS prenant uniquement une entrée numérique d'une longueur maximale

69
Aladin

Déc 2017. Swift 4.

Veillez à ce que une grande partie de l’exemple de code que vous verrez en ligne concernant ce problème soit très obsolète}.

Il vous suffit de coller les éléments suivants dans n’importe quel fichier Swift de votre projet. Nommez le fichier, par exemple, "Handy.Swift"

Cela résout finalement l'un des problèmes les plus stupides d'iOS:

 enter image description here

Vos champs de texte ont maintenant un .maxLength.

Il est tout à fait correct de définir cette valeur dans le storyboard lors du développement ou de la définir dans le code pendant l'exécution de l'application.

// 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?.safelyLimitedTo(length: maxLength)
    }
}

extension String
    {
    func safelyLimitedTo(length n: Int)->String {
        if (self.count <= n) {
            return self
        }
        return String( Array(self).prefix(upTo: n) )
    }
}

C'est si simple.


(Remarque: "overseasLimitedTo # length" est simplement un appel qui réduit la longueur d'une chaîne. Tous les programmeurs Swift ont leur propre extension pour cela, ou, utilisez simplement le mien que vous voyez ici.)


Une version unique encore plus simple ...

La solution ci-dessus corrige les tous champs de texte de votre projet.

Si vous voulez juste que un champ de texte particulier soit simplement limité à "4", c'est tout ...

class PinCodeEntry: UITextField {

    override func didMoveToSuperview() {

        super.didMoveToSuperview()
        addTarget(self, action: #selector(fixMe), for: .editingChanged)
    }

    @objc private func fixMe() { text = text?.safelyLimitedTo(length 4) }
}

Phew! C'est tout ce qu'on peut en dire.

(Just BTW, voici une astuce très utile similaire concernant UITextView, https://stackoverflow.com/a/42333832/294884 )

84
Fattie

Swift 4, utilisez simplement:

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

De la même manière que Steven Schmatz l’a fait, mais avec 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
}
7
Pavlos

Je pense que l'extension est plus pratique pour cela. Voir la réponse complète ici

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
  }

}
6
ZaEeM ZaFaR

Les autres solutions présentées ci-dessus produisent un cycle de conservation en raison de la carte de champ de texte. En outre, la propriété maxLength devrait être nullable si elle n'est pas définie à la place des constructions Int.max artificielles; et la cible sera définie plusieurs fois si maxLength est modifié. 

Voici une solution mise à jour pour Swift4 avec une carte faible pour éviter les fuites de mémoire et les autres correctifs

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
    }
}
4
Angel G. Olloqui

Ma version Swift 4 de 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
    }
2
aviran

Cette réponse est pour Swift 4, et est assez simple avec la possibilité de laisser un retour en arrière.

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

J'ai quelque chose à ajouter à la réponse d'Aladin:

  1. Votre contrôleur de vue doit être conforme à UITextFieldDelegate

    class MyViewController: UIViewController, UITextViewDelegate {
    
    }
    
  2. Définissez le délégué de votre champ de texte: Pour définir le délégué, vous pouvez contrôler le glissement du champ de texte vers votre contrôleur de vue dans le storyboard. Je pense que cela est préférable à le mettre en code

  3. Implémentez la méthode dans votre contrôleur de vue: textField(_:shouldChangeCharactersInRange:replacementString:)

2
SwiftMatt

Je donne une réponse complémentaire basée sur @Frouo. Je pense que sa réponse est la plus belle façon. Parce que c'est un contrôle commun que nous pouvons réutiliser. Et il n'y a pas de problème de fuite ici.

    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
Victor Choy

mise à jour pour cette Fattie answer

import UIKit

extension UITextField {

    /// Runtime 键
    private struct AssociatedKeys {

        static var maxlength: UInt8 = 0
        static var lastString: UInt8 = 0
    }

    /// 最大输入长度
    @IBInspectable var maxLength: Int {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.maxlength) as? Int ?? 0
        }
        set(newValue) {
            objc_setAssociatedObject(self, &AssociatedKeys.maxlength, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            addTarget(self, action: #selector(fix), for: .editingChanged)
        }
    }

    /// 最后一个符合条件的规则
    private var lastQualifiedString: String? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.lastString) as? String
        }
        set(newValue) {
            objc_setAssociatedObject(self, &AssociatedKeys.lastString, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    @objc func fix(textField: UITextField) {

        guard markedTextRange == nil else { return }

        /// 如果 个数 为最大个数
        /// 在字典中,保存 最大数据
        if textField.text?.count == maxLength {

            /// 在 字典中保存 该数值
            lastQualifiedString = textField.text

            // 在 小于个数时 清除 数据
        } else if textField.text?.count ?? 0 < maxLength {
            lastQualifiedString = nil
        }

        let editRange: UITextRange?

        /// 如果 输入框内的个数大于 最大值
        if textField.text?.count ?? 0 > maxLength {

            /// 将选中的 range 向前移动一个位置
            let position = textField.position(from: safeTextPosition(selectedTextRange?.start), offset: -1) ?? textField.beginningOfDocument

            editRange = textField.textRange(from: safeTextPosition(position), to: safeTextPosition(position))
        } else {

            editRange = selectedTextRange
        }

        /// 配置 值
        textField.text = textField.text?.safelyLimitedTo(length: maxLength, safeText: lastQualifiedString)

        textField.selectedTextRange = editRange
    }

    /// 安全获取  UITextPosition
    private func safeTextPosition(_ optionlTextPosition: UITextPosition?) -> UITextPosition {

        /* beginningOfDocument -> The end and beginning of the the text document. */
        return optionlTextPosition ?? self.beginningOfDocument
    }
}

extension String {

    /// 比较后 决定 返回 text 还是 safetext
    ///
    /// - Parameters:
    ///   - n: 长度
    ///   - safeText: 安全字符串
    /// - Returns: 返回的值
    fileprivate func safelyLimitedTo(length n: Int, safeText: String?) -> String? {
        if (self.count <= n) {
            return self
        }
        return safeText ?? String( Array(self).prefix(upTo: n) )
    }
}
1
Z King

Travailler chez Swift4

// STEP 1 set UITextFieldDelegate

    class SignUPViewController: UIViewController , UITextFieldDelegate {

       @IBOutlet weak var userMobileNoTextFiled: UITextField!

        override func viewDidLoad() {
            super.viewDidLoad()

// délégué STEP 2
userMobileNoTextFiled.delegate = self // Définition du délégué }

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

// STEP 3 call 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
Keshav Gera

Solution simple sans utiliser de délégué:

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

Voici une alternative à Swift 3.2+ qui évite les manipulations inutiles de chaînes. Dans ce cas, la longueur maximale est de 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
Greg Brown

J'utilise cette étape, d'abord Définir délégué texfield dans viewdidload.

    override func viewDidLoad() {
        super.viewDidLoad()

        textfield.delegate = self

    }

puis shouldChangeCharactersIn après l’inclusion de 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
Jeri P.M

Il suffit simplement de vérifier avec le nombre de caractères dans la chaîne

  1. Ajouter un délégué pour visualiser le contrôleur et un délégué

class YorsClassName: UITextFieldDelegate {

}

  1. vérifie le nombre de caractères autorisés pour textfield

func textField (_ textField: UITextField, shouldChangeCharactersIn gamme: NSRange, replacementString string: String) -> Bool {si textField.text? .count == 1 {return false} return true}

Note: Ici, je n'ai vérifié que les caractères autorisés dans textField.

0