Ustaw maksymalną długość znaków UITextField w języku Swift

85

Wiem, że są inne tematy na ten temat, ale nie mogę się dowiedzieć, jak to wdrożyć.

Próbuję ograniczyć UITextField do tylko 5 znaków

Najlepiej alfanumeryczne i - i. i _

Widziałem ten kod

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
}

i

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 
}

Po prostu nie wiem, jak to właściwie zaimplementować lub które „pole tekstowe” należy zamienić na moje niestandardowe pole o nazwie UITextField

ishkur88
źródło
Wdrożenie Swift 4 stackoverflow.com/a/46513151/2303865
Leo Dabus
Krótki alert - aby skrócićString w Swift te dni można w końcu po prostu .prefix (n)
fattie

Odpowiedzi:

134
  1. Twój kontroler widoku powinien być zgodny z UITextFieldDelegate, jak poniżej:

    class MyViewController: UIViewController, UITextFieldDelegate {
    
    }
    
  2. Ustaw delegata swojego pola tekstowego: myTextField.delegate = self

  3. Zaimplementuj metodę w kontrolerze widoku: textField(_:shouldChangeCharactersInRange:replacementString:)

Wszyscy razem:

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
}

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

Umożliwienie wpisania tylko określonego zestawu znaków w dane pole tekstowe

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
}

Jak zaprogramować pole tekstowe iOS, które przyjmuje tylko dane liczbowe o maksymalnej długości

Aladin
źródło
Dziękuję bardzo za szybką odpowiedź! Jeśli ustawię to jedno pole tekstowe jako delegata, czy będę mógł modyfikować inne pola tekstowe?
ishkur88
tak, a otrzymasz odpowiednie pole tekstowe (edytowane) jako pierwszy parametr textFieldw metodzie func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
Aladin
Ale gdzie umieścić drugi parametr? Nie odwołuję się ponownie do myTextField po ustawieniu go jako delegata.
ishkur88,
Na przykład, gdybym chciał zrobić kolejne pole tekstowe 0-9 tylko dla numerów telefonów.
ishkur88,
za każdym razem, gdy edytowane jest pole tekstowe, wywoływany shouldChangeCharactersInRangejest callback , to dla wszystkich pól tekstowych, otrzymujesz callback w tym samym miejscu shouldChangeCharactersInRangei wewnątrz tej metody możesz wiedzieć, które pole tekstowe jest edytowane dzięki przekazanemu parametrowi, textFieldktóry możesz na przykład nadać tag dla każdego pola tekstowego i przetestuj wewnątrz, shouldChangeCharactersInRangea dla każdego pola tekstowego przeprowadź walidację zawartości
Aladin
114

Grudnia 2017. Swift 4.

Uważaj, aby przykładowy kod, który zobaczysz w Internecie dotyczący tego problemu, jest bardzo nieaktualny .

Wklej poniższe do dowolnego pliku Swift w swoim projekcie. Możesz dowolnie nazwać plik, na przykład „Handy.swift”

To w końcu rozwiązuje jeden z najgłupszych problemów w iOS:

wprowadź opis obrazu tutaj

Twoje pola tekstowe mają teraz rozszerzenie .maxLength.

Całkowicie w porządku jest ustawić tę wartość w scenorysie podczas programowania lub ustawić ją w kodzie, gdy aplikacja jest uruchomiona.

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

To takie proste.


Przypis - w dzisiejszych czasach, aby bezpiecznie skrócić a Stringszybko, po prostu.prefix(n)


Jeszcze prostsza jednorazowa wersja ...

Powyższe rozwiązuje wszystkie pola tekstowe w projekcie.

Jeśli chcesz, aby jedno konkretne pole tekstowe było ograniczone do „4”, to jest to ...

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

Uff! To wszystko.

(Tak przy okazji, oto podobna bardzo przydatna wskazówka dotycząca widoku UIText , https://stackoverflow.com/a/42333832/294884 )


Dla programisty OCD (jak ja) ...

Jak przypomina @LeoDabus, .prefix zwraca podciąg. Jeśli jesteś niesamowicie troskliwy, to

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

byłoby

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

Cieszyć się!

Fattie
źródło
2
Wydaje się legalne! Dziękuję Ci.
J. Doe,
2
czy jest coś takiego dla textView?
Dory,
13
To wszystko, co trzeba zrobić, aby osiągnąć coś tak powszechnego i tak prostego, zaskakuje mnie. Nie mogli po prostu dać nam prostego wbudowanego textField.maxLength ... w każdym razie Twoje rozwiązanie jest świetne, dzięki!
mylovemhz
2
Rozwiązanie jest przydatne, ale mapa pól tekstowych na górze faktycznie utworzy cykl przechowywania.
Angel G. Olloqui
3
OSTRZEŻENIE DLA KORZYSTAJĄCYCH Z TEGO ROZWIĄZANIA! To rozwiązanie ma pewne problemy. Jedną z nich jest to, że jeśli użytkownik wpisze na początku pola textField, pozwolisz mu wpisać nowy znak, a ostatni zostanie usunięty, a także kursor przeskoczy do ostatniego znaku w polu. Innym problemem jest to, że jeśli ustawisz tekst programowo, pozwoli to ustawić tekst większy niż limit. Inny problem występuje, jeśli cofniesz zmianę (CMD + Z za pomocą klawiatury zewnętrznej), nastąpi awaria, jeśli wcześniej próbowałeś dodać cyfrę powyżej limitu.
juancazalla
26

Swift 4, po prostu użyj:

public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    return range.location < 10
}
Сергей Билык
źródło
powinna to być wybrana odpowiedź. Prosty i dobrze działa.
user832
9
To nie działa. Co się stanie, jeśli dotkniesz środka ciągu i możesz wpisać więcej niż X znaków.
Slavcho
zamiast range.location <10 możesz użyć textField.text.length <10. To rozwiązanie jest proste i eleganckie.
Saqib Saud
Możesz skorzystać z tego rozwiązania: if textField.text? .Count> = 12 {return false}
Сергей Билык
1
nie działa po przejściu tekstu, jeśli chcesz pracować w przeszłej akcji, powinieneś dodaćstring.count < MAX_LENGTH
Reza Dehnavi
12

W ten sam sposób zrobił to Steven Schmatz, ale używając 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
}
Pavlos
źródło
1
Dobra odpowiedź. Doceniony.
Hasya
Dziękuję @Hasya
Pavlos
6

Myślę, że rozszerzenie jest do tego bardziej przydatne. Zobacz pełną odpowiedź tutaj

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
  }

}
ZaEeM ZaFaR
źródło
4

Inne rozwiązania zamieszczone powyżej powodują cykl przechowywania ze względu na mapę tekstową. Poza tym maxLengthwłaściwość powinna mieć wartość null, jeśli nie jest ustawiona zamiast sztucznych Int.maxkonstrukcji; a cel zostanie ustawiony wiele razy, jeśli zmienisz maxLength.

Tutaj zaktualizowane rozwiązanie dla Swift4 ze słabą mapą, aby zapobiec wyciekom pamięci i innym poprawkom

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
    }
}
Angel G. Olloqui
źródło
Dziękuję za odpowiedź. Czy możesz wyjaśnić maxLengths?
Keyhan Kamangar
3

Proste rozwiązanie bez użycia pełnomocnika:

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
        }
}
ober
źródło
2
Odpowiedź, której szukam: D
Ankur Lahiry
1
To rozwiązanie, proste i eleganckie, bez standardowego kodu.
zeeshan
3

Moja wersja 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
    }
aviran
źródło
To powinna być akceptowana odpowiedź. Działa doskonale, bez błędów.
10623169
2

Mam coś do dodania do odpowiedzi Aladina:

  1. Twój kontroler widoku powinien być zgodny z UITextFieldDelegate

    class MyViewController: UIViewController, UITextViewDelegate {
    
    }
    
  2. Ustaw delegata swojego pola tekstowego: Aby ustawić pełnomocnika, możesz kontrolować przeciąganie z pola tekstowego do kontrolera widoku w scenorysie. Myślę, że jest to lepsze niż ustawienie go w kodzie

  3. Zaimplementuj metodę w kontrolerze widoku: textField(_:shouldChangeCharactersInRange:replacementString:)

SwiftMatt
źródło
2

Uzupełniającą odpowiedź udzielam na podstawie @Frouo. Myślę, że jego odpowiedź jest najpiękniejsza. Ponieważ jest to powszechna kontrola, którą możemy ponownie wykorzystać. I tutaj nie ma problemu z wyciekiem.

    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
        }



    }
Victor Choy
źródło
2

aktualizacja dla tej odpowiedzi Fattie

dzięki

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
    }
}
zheng
źródło
1

Praca w Swift4

// KROK 1 ustaw UITextFieldDelegate

    class SignUPViewController: UIViewController , UITextFieldDelegate {

       @IBOutlet weak var userMobileNoTextFiled: UITextField!

        override func viewDidLoad() {
            super.viewDidLoad()

// KROK 2 ustaw delegata
userMobileNoTextFiled.delegate = self // ustaw delegata}

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

// wywołanie funkcji KROKU 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
        }
    }
Keshav Gera
źródło
1

Ta odpowiedź dotyczy Swift 4 i jest dość prosta z możliwością przepuszczenia cofnięcia.

func textField(_ textField: UITextField, 
               shouldChangeCharactersIn range: NSRange, 
               replacementString string: String) -> Bool {
    return textField.text!.count < 10 || string == ""
}
CodeBender
źródło
Nie dotyczy to kopiowania i wklejania
Cornr
1

Po prostu sprawdź liczbę znaków w ciągu

  1. Dodaj pełnomocnika, aby wyświetlić kontroler i jako delegata
    class YorsClassName : UITextFieldDelegate {

    }
  1. sprawdź liczbę znaków dozwolonych dla pola tekstowego
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if textField.text?.count == 1 {
        return false
    }
    return true
}

Uwaga: tutaj sprawdziłem, czy w textField jest tylko znak dozwolony

Akshay Digrase
źródło
1

TextField Limit Character After Block the Text 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


}
Praveen Reddy
źródło
1

Na wszelki wypadek nie zapomnij zabezpieczyć rozmiaru zakresu przed zastosowaniem go do łańcucha. W przeciwnym razie nastąpi awaria, jeśli użytkownik to zrobi:

Wpisz tekst o maksymalnej długości Wstaw coś (Nic nie zostanie wstawione z powodu ograniczenia długości, ale iOS o tym nie wie) Cofnij wstawienie (Wystąpi awaria, ponieważ zakres będzie większy niż rzeczywisty rozmiar ciągu)

Ponadto użytkownicy iOS 13 mogą przypadkowo wywołać to za pomocą gestów

Proponuję dodać to do swojego projektu

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

I użyj tego w ten sposób:

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
}

W przeciwnym razie aplikacja będzie się ciągle zawieszać

kikiwora
źródło
0

Oto alternatywa dla Swift 3.2+, która pozwala uniknąć niepotrzebnych manipulacji na strunach. W takim przypadku maksymalna długość to 10:

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

    return text.count - range.length + string.count <= 10
}
Greg Brown
źródło
0

Używam tego kroku, najpierw Ustaw delegata texfield w viewdidload.

    override func viewDidLoad() {
        super.viewDidLoad()

        textfield.delegate = self

    }

a następnie shouldChangeCharactersIn po uwzględnieniu 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
                }
            }
    }
Jeri PM
źródło
0

Jeśli masz wiele pól textField, które mają różne kontrole długości na jednej stronie, znalazłem łatwe i krótkie rozwiązanie.

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)
    }
}
Reza Dehnavi
źródło