Comprimento máximo UITextField

120

Quando eu tentei Como definir o número máximo de caracteres que podem ser inseridos em um UITextField usando o swift? , Vi que se eu usar todos os 10 caracteres, também não posso apagar o personagem.

A única coisa que posso fazer é cancelar a operação (excluir todos os caracteres juntos).

Alguém sabe como não bloquear o teclado (para que eu não possa adicionar outras letras / símbolos / números, mas eu possa usar o backspace)?

Giorgio Nocera
fonte

Respostas:

294

Com o Swift 5 e o iOS 12, tente a seguinte implementação do textField(_:shouldChangeCharactersIn:replacementString:)método que faz parte do UITextFieldDelegateprotocolo:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    guard let textFieldText = textField.text,
        let rangeOfTextToReplace = Range(range, in: textFieldText) else {
            return false
    }
    let substringToReplace = textFieldText[rangeOfTextToReplace]
    let count = textFieldText.count - substringToReplace.count + string.count
    return count <= 10
}
  • A parte mais importante desse código é a conversão de range( NSRange) para rangeOfTextToReplace( Range<String.Index>). Veja este tutorial em vídeo para entender por que essa conversão é importante.
  • Para fazer este trabalho de código corretamente, você também deve definir o textField's smartInsertDeleteTypevalor a UITextSmartInsertDeleteType.no. Isso impedirá a possível inserção de um espaço extra (indesejado) ao executar uma operação de colagem.

O código de exemplo completo abaixo mostra como implementar textField(_:shouldChangeCharactersIn:replacementString:)em um UIViewController:

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var textField: UITextField! // Link this to a UITextField in Storyboard

    override func viewDidLoad() {
        super.viewDidLoad()

        textField.smartInsertDeleteType = UITextSmartInsertDeleteType.no
        textField.delegate = self
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        guard let textFieldText = textField.text,
            let rangeOfTextToReplace = Range(range, in: textFieldText) else {
                return false
        }
        let substringToReplace = textFieldText[rangeOfTextToReplace]
        let count = textFieldText.count - substringToReplace.count + string.count
        return count <= 10
    }

}
Imanou Petit
fonte
Você acabou de colocar esse código na sua classe de controlador de exibição? Ou tenho que fazer conexões?
Isaac Wasserman
Se alguém precisar colocar alguma condição ... você pode fazer assim .. if (textField .isEqual (mobileNumberTextfield)) {guarda deixe texto = textField.text else {return true} deixe newLength = texto.caracteres.conta + sequência.caracteres.conta - range.length retorne newLength <= limitLength; } retornar verdadeiro;
Narasimha Nallamsetty
5
Para Swift 4, text.characters.counté o uso obsoletotext.count
Mohamed Salah
47

Eu faço assim:

func checkMaxLength(textField: UITextField!, maxLength: Int) {
    if (countElements(textField.text!) > maxLength) {
        textField.deleteBackward()
    }
}

O código funciona para mim. Mas eu trabalho com storyboard. No Storyboard, adiciono uma ação para o campo de texto no controlador de exibição na edição alterada .

Martin
fonte
1
countElements foi alterado para contar no Swift 2, mas mudar isso funciona para mim!
John
1
Obrigado, você pode usar agora textField.text? .Characters.count desde que countElements foi alterado.
Anibal R.
1
Tks, funcionou muito bem com esta mudança: countElements (TextField.text!) Em Swift 2 é:? TextField.text .characters.count
kha
32

Atualização para Swift 4

 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
     guard let text = textField.text else { return true }
     let newLength = text.count + string.count - range.length
     return newLength <= 10
}
Shan Ye
fonte
15

Adicione mais detalhes da resposta @Martin

// linked your button here
@IBAction func mobileTFChanged(sender: AnyObject) {
    checkMaxLength(sender as! UITextField, maxLength: 10)
}

// linked your button here
@IBAction func citizenTFChanged(sender: AnyObject) {
    checkMaxLength(sender as! UITextField, maxLength: 13)
}

func checkMaxLength(textField: UITextField!, maxLength: Int) {
    // swift 1.0
    //if (count(textField.text!) > maxLength) {
    //    textField.deleteBackward()
    //}
    // swift 2.0
    if (textField.text!.characters.count > maxLength) {
        textField.deleteBackward()
    }
}
Sruit A.Suk
fonte
1
count (textField.text!) fornece um erro. Você deve usar textField.text! .Characters.count
Regis St-Gelais
1
Obrigado @ RegisSt-Gelais, Já é uma resposta antiga, atualizei-a agora #
Sruit A.Suk
11

Em Swift 4

Limite de 10 caracteres para o campo de texto e permite excluir (backspace)

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if textField ==  userNameFTF{
            let char = string.cString(using: String.Encoding.utf8)
            let isBackSpace = strcmp(char, "\\b")
            if isBackSpace == -92 {
                return true
            }
            return textField.text!.count <= 9
        }
        return true
    }
Sai kumar Reddy
fonte
8
func checkMaxLength(textField: UITextField!, maxLength: Int) {
        if (textField.text!.characters.count > maxLength) {
            textField.deleteBackward()
        }
}

uma pequena mudança para o IOS 9

Jeremy Andrews
fonte
8

Swift 3

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

            let nsString = NSString(string: textField.text!)
            let newText = nsString.replacingCharacters(in: range, with: string)
            return  newText.characters.count <= limitCount
    }
Basil Mariano
fonte
8

você pode estender o UITextField e adicionar um @IBInspectableobjeto para lidar com isso:

SWIFT 5

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)
        }
    }
    @objc func fix(textField: UITextField) {
        if let t = textField.text {
            textField.text = String(t.prefix(maxLength))
        }
    }
}

e depois defini-lo no inspetor de atributos

insira a descrição da imagem aqui

Ver resposta original do Swift 4

mohsen
fonte
2
Código limpo e agradável. Mas, por algum motivo, isso causa um comportamento estranho de edição quando você usa emojis. O cursor pula para o final da linha toda vez que você tenta editar.
Phontaine Judd 31/12/19
5

Se você deseja sobrescrever a última letra:

let maxLength = 10

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    if range.location > maxLength - 1 {
        textField.text?.removeLast()
    }

    return true
}
maxwell
fonte
4

Postei uma solução usando IBInspectable, para que você possa alterar o valor do comprimento máximo no criador de interface ou programaticamente. Confira aqui

frouo
fonte
3

Você pode usar no swift 5 ou swift 4 como a imagem se parece abaixo insira a descrição da imagem aqui

  1. Adicionar textField no View Controller
  2. Conecte-se ao texto no ViewController
  3. adicione o código na view ViewController

     class ViewController: UIViewController , UITextFieldDelegate {
    
      @IBOutlet weak var txtName: UITextField!
    
      var maxLen:Int = 8;
    
     override func viewDidLoad() {
        super.viewDidLoad()
    
        txtName.delegate = self
       }
    
     func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    
         if(textField == txtName){
            let currentText = textField.text! + string
            return currentText.count <= maxLen
         }
    
         return true;
       }
    }

Você pode fazer o download do formulário de código-fonte completo do GitHub: https://github.com/enamul95/TextFieldMaxLen

Enamul Haque
fonte
1

Cuidado com o bug de desfazer do UITextField mencionado neste post: Defina o comprimento máximo de caracteres de um UITextField

aqui está como você o corrige rapidamente

if(range.length + range.location > count(textField.text)) {
        return false;
}
Horatio
fonte
Se você deseja oferecer suporte a emoji e esse uso: if (range.length + range.location> count (textField.text.utf16)) {return false; }
AlexD
1
Here is my version of code. Hope it helps!

    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        let invalidCharacters = NSCharacterSet(charactersInString: "0123456789").invertedSet

        if let range = string.rangeOfCharacterFromSet(invalidCharacters, options: nil, range:Range<String.Index>(start: string.startIndex, end: string.endIndex))
        {
            return false
        }

        if (count(textField.text) > 10  && range.length == 0)
        {
            self.view.makeToast(message: "Amount entry is limited to ten digits", duration: 0.5, position: HRToastPositionCenter)
            return false
        }
        else
        {

        }

        return true
    }
AG
fonte
1
Eu gosto da extensão Toast UIView :)
Regis St-Gelais
1

Eu tenho usado esse protocolo / extensão em um dos meus aplicativos e é um pouco mais legível. Eu gosto de como ele reconhece backspaces e explicitamente diz quando um personagem é um backspace.

Algumas coisas a considerar:

1. O que implementa essa extensão de protocolo precisa especificar um limite de caracteres.Esse normalmente é o seu ViewController, mas você pode implementar o limite de caracteres como uma propriedade calculada e retornar outra coisa, por exemplo, um limite de caracteres em um dos seus modelos.

2. Você precisará chamar esse método dentro do método delegado shouldChangeCharactersInRange do seu campo de texto. Caso contrário, você não poderá bloquear a entrada de texto retornando false etc.

3. Você provavelmente desejará permitir caracteres de backspace. Por isso, adicionei a função extra para detectar backspaces. Seu método shouldChangeCharacters pode verificar isso e retornar 'true' desde o início para permitir sempre backspaces.

protocol TextEntryCharacterLimited{
    var characterLimit:Int { get } 
}

extension TextEntryCharacterLimited{

    func charactersInTextField(textField:UITextField, willNotExceedCharacterLimitWithReplacementString string:String, range:NSRange) -> Bool{

        let startingLength = textField.text?.characters.count ?? 0
        let lengthToAdd = string.characters.count
        let lengthToReplace = range.length

        let newLength = startingLength + lengthToAdd - lengthToReplace

        return newLength <= characterLimit

    }

    func stringIsBackspaceWith(string:String, inRange range:NSRange) -> Bool{
        if range.length == 1 && string.characters.count == 0 { return true }
        return false
    }

}

Se algum de vocês estiver interessado, eu tenho um repositório do Github em que peguei parte desse comportamento de limite de caracteres e coloquei em uma estrutura iOS. Existe um protocolo que você pode implementar para obter uma exibição de limite de caracteres semelhante ao Twitter que mostra até que ponto você ultrapassou o limite de caracteres.

Framework CharacterLimited no Github

Theo Bendixson
fonte
1

Como os delegados são um relacionamento 1 para 1 e talvez eu queira usá-lo em outro local por outros motivos, gosto de restringir o tamanho do campo de texto adicionando esse código em sua configuração:

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        setup()
    }

    required override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    func setup() {

        // your setup...

        setMaxLength()
    }

    let maxLength = 10

    private func setMaxLength() {
            addTarget(self, action: #selector(textfieldChanged(_:)), for: UIControlEvents.editingChanged)
        }

        @objc private func textfieldChanged(_ textField: UITextField) {
            guard let text = text else { return }
            let trimmed = text.characters.prefix(maxLength)
            self.text = String(trimmed)

        }
MQLN
fonte
0

Estou usando isso;

Limite de 3 caracteres

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

        if let txt = textField.text {
            let currentText = txt + string
            if currentText.count > 3 {
                return false
            }
            return true
        }
        return true
    }
alicanozkara
fonte
0

Swift 5

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        let MAX_LENGTH = 4
        let updatedString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
        return updatedString.count <= MAX_LENGTH
    }
TDK
fonte
-3

Você precisa verificar se a cadeia existente mais a entrada é maior que 10.

   func textField(textField: UITextField!,shouldChangeCharactersInRange range: NSRange,    replacementString string: String!) -> Bool {
      NSUInteger newLength = textField.text.length + string.length - range.length;
      return !(newLength > 10)
   }
bhzag
fonte
5
Seu código está errado. 1. Você deve declarar sua constante ou variável com let ou var no Swift (não no NSUInteger). 2. textField.text e string são do tipo String. O comprimento não é uma propriedade / método de String no Swift.
Imanou Petit