Legen Sie die maximale Zeichenlänge eines UITextField in Swift fest


85

Ich weiß, dass es noch andere Themen gibt, aber ich kann anscheinend nicht herausfinden, wie ich es implementieren soll.

Ich versuche, ein UITextField auf nur 5 Zeichen zu beschränken

Vorzugsweise alphanumerisch und - und. und _

Ich habe diesen Code gesehen

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
}

und

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 
}

Ich weiß nur nicht, wie ich es tatsächlich implementieren soll oder welches "Textfeld" ich gegen mein benutzerdefiniertes UITextField austauschen soll



Schnelle Warnung - um eine Stringin Swift in diesen Tagen zu verkürzen , können Sie endlich nur .prefix (n)
Fattie

Antworten:


133
  1. Ihr View Controller sollte wie folgt übereinstimmen UITextFieldDelegate:

    class MyViewController: UIViewController, UITextFieldDelegate {
    
    }
    
  2. Legen Sie den Delegaten Ihres Textfelds fest: myTextField.delegate = self

  3. Implementieren Sie die Methode in Ihrem View Controller: textField(_:shouldChangeCharactersInRange:replacementString:)

Alle zusammen:

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
}

Für 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
}

Zulassen, dass nur ein bestimmter Zeichensatz in ein bestimmtes Textfeld eingegeben wird

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
}

So programmieren Sie ein iOS-Textfeld, das nur numerische Eingaben mit maximaler Länge akzeptiert


Vielen Dank für die prompte Antwort! Wenn ich dieses eine Textfeld als Delegaten festlegen würde, könnte ich dann andere Textfelder ändern?
ishkur88

Ja, und Sie erhalten das betreffende Textfeld (wird bearbeitet) als ersten Parameter textFieldin der Methode func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
Aladin

Aber wo würde ich den zweiten Parameter eingeben? Ich verweise nicht mehr auf myTextField, nachdem ich es als Delegat festgelegt habe.
ishkur88

Zum Beispiel, wenn ich ein anderes Textfeld 0-9 nur für Telefonnummern erstellen wollte.
ishkur88

Jedes Mal, wenn ein Textfeld bearbeitet wird, wird der Rückruf shouldChangeCharactersInRangeaufgerufen. Dies gilt für alle Textfelder. Sie erhalten den Rückruf an derselben Stelle. shouldChangeCharactersInRangeInnerhalb dieser Methode können Sie dank des übergebenen Parameters, den textFieldSie beispielsweise angeben können, feststellen, welches Textfeld bearbeitet wird Tag für jedes Textfeld und Test innerhalb des shouldChangeCharactersInRangeund für jedes Textfeld führen Sie die Validierung des Inhalts durch
Aladin

113

Dezember 2017. Swift 4.

Achten Sie darauf, dass ein Großteil des Beispielcodes, den Sie online zu diesem Problem sehen, sehr veraltet ist .

Fügen Sie Folgendes in eine beliebige Swift-Datei in Ihrem Projekt ein. Sie können der Datei einen beliebigen Namen geben, z. B. "Handy.swift".

Dies behebt schließlich eines der dümmsten Probleme in iOS:

Geben Sie hier die Bildbeschreibung ein

Ihre Textfelder haben jetzt eine .maxLength.

Es ist völlig in Ordnung, diesen Wert während der Entwicklung im Storyboard oder während der Ausführung der App im Code festzulegen.

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

So einfach ist das.


Fußnote - heutzutage, um ein Stringschnelles sicheres Abschneiden zu erreichen, müssen Sie einfach.prefix(n)


Eine noch einfachere einmalige Version ...

Das obige korrigiert alle Textfelder in Ihrem Projekt.

Wenn Sie nur möchten, dass ein bestimmtes Textfeld einfach auf "4" beschränkt ist, dann ist das ...

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

Puh! Das ist alles dazu.

(Übrigens, hier ist ein ähnlicher sehr nützlicher Tipp in Bezug auf UIText View , https://stackoverflow.com/a/42333832/294884 )


Für den OCD-Programmierer (wie ich) ...

Wie @LeoDabus erinnert, gibt .prefix einen Teilstring zurück. Wenn Sie sich unglaublich sorgen, ist dies

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

wäre

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

Genießen!


2
Klingt plausibel! Vielen Dank.
J. Doe

2
Gibt es so etwas für textView?
Dory

13
Dass all dies getan werden muss, um etwas so Gemeinsames und Einfaches zu erreichen, ist mir ein Rätsel. Sie konnten uns nicht einfach eine einfache integrierte textField.maxLength geben ... trotzdem ist Ihre Lösung großartig, danke!
Mylovemhz

2
Die Lösung ist praktisch, aber die Karte der Textfelder oben erzeugt tatsächlich einen Aufbewahrungszyklus.
Angel G. Olloqui

3
WARNUNG FÜR DIE, DIE DIESE LÖSUNG VERWENDEN! Diese Lösung hat einige Probleme. Eine davon ist, dass wenn der Benutzer am Anfang des Textfelds tippt, Sie ihm erlauben werden, das neue Zeichen einzugeben, und das letzte Zeichen entfernt wird. Außerdem springt der Cursor zum letzten Zeichen im Feld. Ein weiteres Problem besteht darin, dass Sie, wenn Sie den Text programmgesteuert festlegen, einen Text festlegen können, der größer als der Grenzwert ist. Ein weiteres Problem tritt auf, wenn Sie eine Änderung rückgängig machen (CMD + Z mit einer externen Tastatur). Sie stürzt ab, wenn Sie zuvor versucht haben, eine Ziffer über dem Grenzwert hinzuzufügen.
Juancazalla

26

Swift 4, benutze einfach:

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

Dies sollte die ausgewählte Antwort sein. Einfach und funktioniert gut.
user832

9
Es funktioniert nicht. Was ist, wenn Sie in die Mitte der Zeichenfolge tippen und mehr als X Zeichen eingeben können?
Slavcho

Anstelle von range.location <10 können Sie textField.text.length <10 verwenden. Diese Lösung ist einfach und elegant.
Saqib Saud

Sie können diese Lösung verwenden: if textField.text? .Count> = 12 {return false}
3ергей Билык

1
Es funktioniert nicht, wenn Sie einen Text hinter sich haben. Wenn Sie in einer früheren Aktion arbeiten möchten, sollten Sie hinzufügenstring.count < MAX_LENGTH
Reza Dehnavi

12

Genauso wie Steven Schmatz es getan hat, aber mit 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
Gute Antwort. Geschätzt.
Hasya

Vielen Dank, dass Sie @Hasya
Pavlos


6

Ich denke, Erweiterung ist dafür praktischer. Die vollständige Antwort finden Sie hier

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

Andere oben angegebene Lösungen erzeugen aufgrund der Textfeldkarte einen Aufbewahrungszyklus. Außerdem sollte die maxLengthEigenschaft nullbar sein, wenn sie nicht anstelle künstlicher Int.maxKonstruktionen festgelegt wird. und das Ziel wird mehrmals festgelegt, wenn maxLength geändert wird.

Hier eine aktualisierte Lösung für Swift4 mit einer schwachen Map, um Speicherlecks und andere Korrekturen zu verhindern

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

Vielen Dank für Ihre Antwort. Können Sie maxLengths bitte erklären?
Keyhan Kamangar

3

Einfache Lösung ohne Delegate:

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
Die Antwort, die ich suche: D
Ankur Lahiry

1
Dies ist die einfache und elegante Lösung ohne Boilerplate-Code.
Zeeshan

3

Meine Swift 4 Version von 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
    }

Dies sollte die akzeptierte Antwort sein. Funktioniert perfekt, ohne Fehler.
10623169

2

Ich habe Aladins Antwort etwas hinzuzufügen:

  1. Ihr View Controller sollte den Anforderungen entsprechen UITextFieldDelegate

    class MyViewController: UIViewController, UITextViewDelegate {
    
    }
    
  2. Festlegen des Delegaten Ihres Textfelds: Um den Delegaten festzulegen, können Sie das Ziehen vom Textfeld auf Ihren Ansichtscontroller im Storyboard steuern. Ich denke, dies ist der Einstellung im Code vorzuziehen

  3. Implementieren Sie die Methode in Ihrem View Controller: textField(_:shouldChangeCharactersInRange:replacementString:)


2

Ich gebe eine ergänzende Antwort basierend auf @Frouo. Ich denke, seine Antwort ist der schönste Weg. Weil es eine übliche Kontrolle ist, die wir wiederverwenden können. Und hier gibt es kein Leckproblem.

    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

Update für diese Fattie-Antwort

Vielen Dank

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

Arbeiten in Swift4

// SCHRITT 1 setze UITextFieldDelegate

    class SignUPViewController: UIViewController , UITextFieldDelegate {

       @IBOutlet weak var userMobileNoTextFiled: UITextField!

        override func viewDidLoad() {
            super.viewDidLoad()

//
SCHRITT 2 Delegat setzen userMobileNoTextFiled.delegate = self // Delegat setzen}

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

// SCHRITT 3 Funktion aufrufen

        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

Diese Antwort ist für Swift 4 und ist ziemlich einfach mit der Fähigkeit, die Rücktaste durchzulassen.

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

Dies behandelt nicht das Kopieren und Einfügen
Cornr

1

Überprüfen Sie einfach die Anzahl der Zeichen in der Zeichenfolge

  1. Fügen Sie einen Delegaten hinzu, um den Controller anzuzeigen und einen Delegaten zuzuweisen
    class YorsClassName : UITextFieldDelegate {

    }
  1. Überprüfen Sie die Anzahl der Zeichen, die für das Textfeld zulässig sind
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if textField.text?.count == 1 {
        return false
    }
    return true
}

Hinweis: Hier habe ich nur nach Zeichen gesucht, die in textField zulässig sind


1

TextField-Begrenzungszeichen nach dem Blockieren des Texts 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

Vergessen Sie für alle Fälle nicht, die Bereichsgröße zu schützen, bevor Sie sie auf die Zeichenfolge anwenden. Andernfalls kommt es zum Absturz, wenn der Benutzer dies tut:

Text mit maximaler Länge eingeben Etwas einfügen (Aufgrund der Längenbeschränkung wird nichts eingefügt, aber iOS weiß nichts davon.) Einfügen rückgängig machen (Sie stürzen ab, da der Bereich größer als die tatsächliche Zeichenfolgengröße ist.)

Bei Verwendung von iOS 13 können Benutzer dies versehentlich durch Gesten auslösen

Ich schlage vor, dass Sie dies zu Ihrem Projekt hinzufügen

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

Und benutze es so:

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
}

Andernfalls stürzt Ihre App ständig ab


0

Hier ist eine Swift 3.2+ Alternative, die unnötige Manipulationen an Zeichenfolgen vermeidet. In diesem Fall beträgt die maximale Länge 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

Ich benutze diesen Schritt, zuerst setze delegate texfield in viewdidload.

    override func viewDidLoad() {
        super.viewDidLoad()

        textfield.delegate = self

    }

und dann shouldChangeCharactersIn, nachdem Sie UITextFieldDelegate eingefügt haben.

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

Wenn Sie mehrere Textfelder mit verschiedenen Längenprüfungen auf einer Seite haben, habe ich eine einfache und kurze Lösung gefunden.

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)
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.