Imposta la lunghezza massima dei caratteri di un UITextField in Swift


88

So che ci sono altri argomenti su questo, ma non riesco a scoprire come implementarlo.

Sto cercando di limitare un UITextField a soli 5 caratteri

Preferibilmente alfanumerico e - e. e _

Ho visto questo codice

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
}

e

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 
}

Non so come implementarlo effettivamente o quale "campo di testo" dovrei sostituire con il mio nome personalizzato UITextField



Avviso rapido: per abbreviare un Stringin Swift in questi giorni puoi finalmente solo .prefix (n)
Fattie

Risposte:


136
  1. Il controller della vista dovrebbe essere conforme a UITextFieldDelegate, come di seguito:

    class MyViewController: UIViewController, UITextFieldDelegate {
    
    }
    
  2. Imposta il delegato del tuo campo di testo: myTextField.delegate = self

  3. Implementa il metodo nel tuo controller di visualizzazione: textField(_:shouldChangeCharactersInRange:replacementString:)

Tutti insieme:

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
}

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

Consentire solo l'inserimento di un determinato set di caratteri in un determinato campo di testo

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
}

Come programmare un campo di testo iOS che accetta solo input numerici con una lunghezza massima


Grazie mille per la pronta risposta! Se imposto questo campo di testo come delegato sarei in grado di modificare altri campi di testo?
ishkur88

sì, e otterrai il campo di testo in questione (in fase di modifica) come primo parametro textFieldnel metodo func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
Aladin

Ma dove metterei il secondo parametro? Non faccio nuovamente riferimento a myTextField dopo averlo impostato come delegato.
ishkur88

Come se volessi creare un altro campo di testo 0-9 solo per i numeri di telefono.
ishkur88

ogni volta che un campo di testo viene modificato, shouldChangeCharactersInRangeviene chiamato il callback , questo è per tutti i campi di testo, ricevi il callback nello stesso posto shouldChangeCharactersInRangee all'interno di questo metodo puoi sapere quale campo di testo viene modificato grazie al parametro passato textFieldpuoi ad esempio dare un tag per ogni campo di testo e test all'interno del campo di testo shouldChangeCharactersInRangee per ogni campo di testo eseguire la convalida del contenuto
Aladin

118

Modern Swift

Si noti che gran parte del codice di esempio in linea relativo a questo problema è estremamente obsoleto .

Incolla quanto segue in qualsiasi file Swift nel tuo progetto. (Puoi assegnare al file un nome qualsiasi, ad esempio "Handy.swift".)

Questo risolve finalmente uno dei problemi più stupidi in iOS:

inserisci qui la descrizione dell'immagine

I tuoi campi di testo ora hanno l'estensione .maxLength.

È del tutto OK impostare quel valore nello storyboard durante lo sviluppo o impostarlo nel codice mentre l'app è in esecuzione.

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

È così semplice.


Nota a piè di pagina: in questi giorni per troncare in modo sicuro un Stringin swift, semplicemente.prefix(n)


Una versione unica ancora più semplice ...

Quanto sopra risolve tutti i campi di testo nel tuo progetto.

Se vuoi solo limitare un campo di testo particolare a dire "4", e questo è tutto ...

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

Phew! È tutto quello che c'è da fare.

(Solo BTW, ecco un suggerimento molto utile simile relativo a UIText View , https://stackoverflow.com/a/42333832/294884 )


Per il programmatore OCD (come me) ...

Come ricorda @LeoDabus, .prefixrestituisce una sottostringa. Se sei incredibilmente premuroso, questo

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

sarebbe

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

Godere!


2
Sembra giusto! Grazie.
J. Doe

13
Che tutto ciò debba essere fatto per ottenere qualcosa di così comune e così semplice mi fa impazzire. Non potevano semplicemente darci un semplice textField.maxLength built-in ... comunque la tua soluzione è fantastica, grazie!
mylovemhz

1
Wow, il miglior consiglio che abbia mai avuto su SO. Lo voterei fino a 100 se potessi!
jonathan3087

2
La soluzione è pratica ma la mappa dei campi di testo in alto produrrà effettivamente un ciclo di conservazione.
Angel G. Olloqui

3
ATTENZIONE PER CHI UTILIZZA QUESTA SOLUZIONE! Questa soluzione presenta alcuni problemi. Uno di questi è che se l'utente digita all'inizio del textField, gli consentirai di digitare il nuovo carattere e l'ultimo verrà rimosso, inoltre, il cursore salterà all'ultimo carattere nel campo. Un altro problema è che se imposti il ​​testo in modo programmatico, ti consentirà di impostare un testo più grande del limite. Un altro problema si verifica se annulli una modifica (CMD + Z con una tastiera esterna) si blocca se hai provato ad aggiungere una cifra oltre il limite in precedenza.
juancazalla

25

Swift 4, usa semplicemente:

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

questa dovrebbe essere la risposta selezionata. Semplice e funziona bene.
user832

9
Non funziona. Cosa succede se tocchi al centro della stringa e puoi digitare più di X caratteri.
Slavcho

invece di range.location <10, puoi usare textField.text.length <10. Questa soluzione è semplice ed elegante.
Saqib Saud

Puoi usare questa soluzione: if textField.text? .Count> = 12 {return false}
Сергей Билык

1
non funziona quando si è passato un testo se si desidera lavorare in azioni passate è necessario aggiungerestring.count < MAX_LENGTH
Reza Dehnavi

12

Nello stesso modo in cui lo ha fatto Steven Schmatz, ma utilizzando 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
Buona risposta. Apprezzato.
Hasya

Grazie @Hasya
Pavlos

9

Per Swift 5:
basta scrivere una riga per impostare la lunghezza massima del carattere:

 self.textField.maxLength = 10

Per maggiori dettagli clicca qui

Credito: http://www.swiftdevcenter.com/max-character-limit-of-uitextfield-and-allowed-characters-swift/


Sebbene questa soluzione possa sembrare semplice, in realtà è necessario molto più codice per implementarla. Questa risposta non è molto utile di per sé e l'aggiunta di una spiegazione e l'inclusione di altri pezzi di codice rilevanti potrebbe essere utile.
FontFamily

6

Penso che l'estensione sia più utile per questo. Vedi la risposta completa qui

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

Altre soluzioni pubblicate sopra producono un ciclo di conservazione a causa della mappa del campo di testo. Inoltre, la maxLengthproprietà dovrebbe essere annullabile se non impostata invece di Int.maxcostruzioni artificiali ; e l'obiettivo verrà impostato più volte se si modifica maxLength.

Ecco una soluzione aggiornata per Swift4 con una mappa debole per prevenire perdite di memoria e altre correzioni

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

Grazie per la tua risposta, puoi spiegare maxLengths per favore?
Keyhan Kamangar,

4

Soluzione semplice senza utilizzare delegato:

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
La risposta che sto cercando: D
Ankur Lahiry

1
Questa è la soluzione, semplice ed elegante, senza codice boilerplate.
zeeshan

3

La mia versione Swift 4 di 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
    }

Questa dovrebbe essere la risposta accettata. Funziona perfettamente, senza bug.
10623169

2

Ho qualcosa da aggiungere alla risposta di Aladin:

  1. Il controller della vista dovrebbe essere conforme a UITextFieldDelegate

    class MyViewController: UIViewController, UITextViewDelegate {
    
    }
    
  2. Imposta il delegato del tuo campo di testo: per impostare il delegato, puoi controllare il trascinamento dal campo di testo al controller della vista nello storyboard. Penso che questo sia preferibile impostarlo nel codice

  3. Implementa il metodo nel tuo controller di visualizzazione: textField(_:shouldChangeCharactersInRange:replacementString:)


2

Fornisco una risposta supplementare basata su @Frouo. Penso che la sua risposta sia il modo più bello. Perché è un controllo comune che possiamo riutilizzare. E non ci sono problemi di perdite qui.

    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

Controlla semplicemente il numero di caratteri nella stringa

  1. Aggiungere un delegato per visualizzare il controller e un delegato assegnato
    class YorsClassName : UITextFieldDelegate {

    }
  1. controlla il numero di caratteri consentiti per textfield
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if textField.text?.count == 1 {
        return false
    }
    return true
}

Nota: qui ho controllato solo i caratteri consentiti in textField


2

aggiornamento per questa risposta Fattie

Grazie

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

Lavorare in Swift4

// PASSAGGIO 1 imposta UITextFieldDelegate

    class SignUPViewController: UIViewController , UITextFieldDelegate {

       @IBOutlet weak var userMobileNoTextFiled: UITextField!

        override func viewDidLoad() {
            super.viewDidLoad()

// PASSAGGIO 2 set delegate
userMobileNoTextFiled.delegate = self // set delegate}

     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 richiama la funz

        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

Questa risposta è per Swift 4 ed è piuttosto semplice con la possibilità di lasciare passare il backspace.

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

Questo non gestisce il copia e incolla
cornr

1

Carattere limite campo testo dopo aver bloccato il testo in 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

Per ogni evenienza, non dimenticare di controllare la dimensione dell'intervallo prima di applicarlo alla stringa. Altrimenti, si verificherà un arresto anomalo se l'utente farà questo:

Digita il testo di lunghezza massima Inserisci qualcosa (non verrà inserito nulla a causa della limitazione della lunghezza, ma iOS non lo sa) Annulla l'inserimento (si verifica un arresto anomalo, perché l'intervallo sarà maggiore della dimensione effettiva della stringa)

Inoltre, l'utilizzo di iOS 13 gli utenti possono attivarlo accidentalmente tramite gesti

Ti suggerisco di aggiungere al tuo progetto questo

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

E usalo in questo modo:

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
}

Altrimenti, verrai costantemente bloccato nella tua app


0

Ecco un'alternativa a Swift 3.2+ che evita la manipolazione delle stringhe non necessaria. In questo caso, la lunghezza massima è 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

Uso questo passaggio, prima imposta texfield delegato in viewdidload.

    override func viewDidLoad() {
        super.viewDidLoad()

        textfield.delegate = self

    }

e quindi shouldChangeCharactersIn dopo aver incluso 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

Se hai più textField che hanno vari controlli di lunghezza su una pagina, ho trovato una soluzione semplice e breve.

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)
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.