web-dev-qa-db-fra.com

Comment limiter UITextField à ne prendre que des nombres dans Swift?

Je veux que l'utilisateur entre uniquement des valeurs numériques dans une UITextField. Sur iPhone, nous pouvons afficher le clavier numérique, mais sur iPad, l'utilisateur peut basculer sur n'importe quel clavier.

Est-il possible d'empêcher l'utilisateur de ne saisir que des valeurs numériques dans une variable UITextField?

56
user5034941

Voici mes 2 cents. (Testé sur Swift 2 uniquement)

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

  let aSet = NSCharacterSet(charactersInString:"0123456789").invertedSet
  let compSepByCharInSet = string.componentsSeparatedByCharactersInSet(aSet)
  let numberFiltered = compSepByCharInSet.joinWithSeparator("")
  return string == numberFiltered

}

C'est juste un peu plus strict. Pas de point décimal non plus.

J'espère que ça aide :) 

PS: Je suppose que vous avez quand même soigné le délégué.

Mise à jour: Swift 3.0 :

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

    let aSet = NSCharacterSet(charactersIn:"0123456789").inverted
    let compSepByCharInSet = string.components(separatedBy: aSet)
    let numberFiltered = compSepByCharInSet.joined(separator: "")
    return string == numberFiltered
}
72
Mr H

Solution pour Swift 3.0 et supérieur

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
  {
    let allowedCharacters = CharacterSet.decimalDigits
    let characterSet = CharacterSet(charactersIn: string)
    return allowedCharacters.isSuperset(of: characterSet)
  }
59
Hiren Panchal

Swift 2.0

Pour ne permettre que des nombres et un "." décimale dans uitextfield.

func textField(textField: UITextField,shouldChangeCharactersInRange range: NSRange,replacementString string: String) -> Bool
{
    let newCharacters = NSCharacterSet(charactersInString: string)
    let boolIsNumber = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(newCharacters)
    if boolIsNumber == true {
        return true
    } else {
        if string == "." {
            let countdots = textField.text!.componentsSeparatedByString(".").count - 1
            if countdots == 0 {
                return true
            } else {
                if countdots > 0 && string == "." {
                    return false
                } else {
                    return true
                }
            }
        } else {
            return false
        }
    }
}
11
Cian

Dans Swift 4.1 et Xcode 9.4.1

Ajoutez UITextFieldDelegate à votre classe

class YourViewController: UIViewController, UITextFieldDelegate

Puis écrivez ce code dans votre viewDidLoad ()

mobileNoTF.delegate = self

Écrire cette fonction de délégué textfield

//MARK - UITextField Delegates
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    //For mobile numer validation
    if textField == mobileNoTF {
        let allowedCharacters = CharacterSet(charactersIn:"+0123456789 ")//Here change this characters based on your requirement
        let characterSet = CharacterSet(charactersIn: string)
        return allowedCharacters.isSuperset(of: characterSet)
    }
    return true
}
11
iOS

Accepter les valeurs décimales dans les champs de texte avec un seul point (.) Dans Swift 3

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let inverseSet = NSCharacterSet(charactersIn:"0123456789").inverted

    let components = string.components(separatedBy: inverseSet)

    let filtered = components.joined(separator: "")

    if filtered == string {
        return true
    } else {
        if string == "." {
            let countdots = textField.text!.components(separatedBy:".").count - 1
            if countdots == 0 {
                return true
            }else{
                if countdots > 0 && string == "." {
                    return false
                } else {
                    return true
                }
            }
        }else{
            return false
        }
    }
}
8
Raj Joshi
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    // return true if the replacementString only contains numeric characters
    let digits = NSCharacterSet.decimalDigitCharacterSet()
    for c in string {
        if !digits.characterIsMember(c) {
            return false
        }
    }

    return true
}

Cette solution fonctionnera même si l'utilisateur change de clavier ou tente de coller une chaîne non numérique dans le champ de texte.

Veillez à définir la propriété delegate du champ de texte approprié.

6
ndmeiri

Solution uniquement iPhone

Quel que soit le champ UITextField à partir duquel vous obtenez ces valeurs, vous pouvez spécifier le type de clavier à afficher lorsque quelqu'un touche au sein du champ de texte.

PAR EXEMPLE. un clavier uniquement numérique.

Comme cette capture d'écran:

 enter image description here

Ipad

L'iPad ne prend pas en charge le clavier numérique. Par conséquent, vous avez le choix entre ne pas prendre en charge l'iPad, valider le champ post-soumission ou suivre l'une des autres suggestions présentées ici pour créer des comportements sains lors de l'exécution sur un iPad.

6
Jamil Hasnine Tamim

Voici une solution simple, vous devez connecter l'événement "Modification modifiée" à cette méthode dans votre contrôleur.

Swift 4

@IBAction func valueChanged(_ sender: UITextField) {
    if let last = sender.text?.last {
        let zero: Character = "0"
        let num: Int = Int(UnicodeScalar(String(last))!.value - UnicodeScalar(String(zero))!.value)
        if (num < 0 || num > 9) {
            //remove the last character as it is invalid
            sender.text?.removeLast()
        }
    }
}
3
shbli

Utiliser le formateur de nombres

Swift 4.x  

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
      let s = NSString(string: textField.text ?? "").replacingCharacters(in: range, with: string)
      guard !s.isEmpty else { return true }
      let numberFormatter = NumberFormatter()
      numberFormatter.numberStyle = .none
      return numberFormatter.number(from: s)?.intValue != nil
 }
3
SPatel

Bien que la plupart de ces solutions fonctionnent, sachez que dans certaines localisations, les décimales sont séparées par un "," et non un "."

La façon la plus propre de le faire serait

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let decimalCharacter = NSNumberFormatter().decimalSeparator
    let characterSet = NSMutableCharacterSet.decimalDigitCharacterSet()
    characterSet.addCharactersInString(decimalCharacter)

    return replacementString.rangeOfCharacterFromSet(characterSet.invertedSet) == nil
}
2
AlexLittlejohn

Testé dans Swift 3.0

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool

{         
    let numberOnly = NSCharacterSet.init(charactersIn: "0123456789")
    let stringFromTextField = NSCharacterSet.init(charactersIn: string)
    let strValid = numberOnly.isSuperset(of: stringFromTextField as CharacterSet)

     return strValid
 }
2
iosLearner

Vous devez d'abord hériter de la classe UITextFieldDelegate avec votre propre propriétaire classe

class ViewController: UIViewController, UITextFieldDelegate {

2ème ajouter un IBOutlet

@IBOutlet weak var firstName: UITextField!

3ème vous devez vous assurer que cet objet utilise

override func viewDidLoad() {
        super.viewDidLoad()
   firstName.delegate = self
}


func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if textField == firstName {
                let allowedCharacters = "1234567890"
                let allowedCharacterSet = CharacterSet(charactersIn: allowedCharacters)
                let typedCharacterSet = CharacterSet(charactersIn: string)
                let alphabet = allowedCharacterSet.isSuperset(of: typedCharacterSet)
                return alphabet


      }
  }
2
akbar khan

En fait, lorsque je travaillais dans le livre de Big Nerd Ranch, ma solution est la suivante:

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

    let newCharacters = NSCharacterSet(charactersInString: string)
    return NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(newCharacters)
}

cela permet seulement les nombres 0-9, pour permettre le "." aussi est plus compliqué que vous ne pouvez en permettre un "."

2
sgib
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    if let numRange = string.rangeOfCharacterFromSet(NSCharacterSet.letterCharacterSet()) {
        return false
    } else {
        return true
    }
   }
2
likarson

Étendez votre contrôleur de vue comme ceci:

class MyViewController: UIViewController, UITextFieldDelegate

Dans la fonction viewDidLoad, étendez votre champ de texte comme ceci:

myTextField.delegate = self

Et puis utilisez la fonction suivante:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let isNumber = CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: string))
    let withDecimal = (
        string == NumberFormatter().decimalSeparator &&
        textField.text?.contains(string) == false
    )
    return isNumber || withDecimal
}

Cela va maintenant s'assurer que l'utilisateur ne peut entrer que des chiffres décimaux.

Swift 4 + Accepte uniquement le nombre .__ et accepte un séparateur.

2
Aftab Ahmed

Pour n'autoriser que les nombres et un seul opérateur décimal, vous pouvez utiliser cette solution:

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let isNumber = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(NSCharacterSet(charactersInString: string))

    return isNumber || (string == NSNumberFormatter().decimalSeparator && textField.text?.containsString(string) == false)
}
2
hprione

Pour permettre à certains personnages

func CheckAddress(string:String) -> Bool  {
        let numberOnly = NSCharacterSet.init(charactersIn: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-@,&#/")
        let stringFromTextField = NSCharacterSet.init(charactersIn: string)
        return numberOnly.isSuperset(of: stringFromTextField as CharacterSet)
    }

print("\(CheckAddress(string: "123"))") //True
print("\(CheckAddress(string: "asdf-"))") //True
print("\(CheckAddress(string: "asd123$"))") //false
1
Ilesh
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool

        {
            let textString = (textField.text! as NSString).replacingCharacters(in: range, with: string)

            if textField == self.phoneTextField  && string.characters.count > 0{
                let numberOnly = NSCharacterSet.decimalDigits
                let strValid = numberOnly.contains(UnicodeScalar.init(string)!)
                return strValid && textString.characters.count <= 10
            }
            return true
        }

dans le code ci-dessus fonctionne dans Swift 3 
NSCharacterSet .decimalDigits
Vous utilisez également des lettres uniquement 
NSCharacterSet .Lettres
et majuscules, minuscules et alphanumériques, espaces est utilisé avec le même code. ou Voir le lien

1

J'ai édité la version de Raj Joshi pour permettre un point ou une virgule:

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

    let inverseSet = CharacterSet(charactersIn:"0123456789").inverted
    let components = string.components(separatedBy: inverseSet)
    let filtered = components.joined(separator: "")

    if filtered == string {
        return true
    } else {
        if string == "." || string == "," {
            let countDots = textField.text!.components(separatedBy:".").count - 1
            let countCommas = textField.text!.components(separatedBy:",").count - 1

            if countDots == 0 && countCommas == 0 {
                return true
            } else {
                return false
            }
        } else  {
            return false
        }
    }
}
1
Matthijs

Comme s'il n'y avait pas assez de réponses, voici la mienne. Je pense que tous les exemples autorisés pour les séparateurs décimaux sont défectueux en termes de localisation, de backspaces ou de copier/coller.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    if string.isEmpty {return true} //allow for backspace

    let decimalSeparator = NSNumberFormatter().decimalSeparator ?? "."
    let validChars = NSMutableCharacterSet(charactersInString: decimalSeparator)
    validChars.formUnionWithCharacterSet(NSCharacterSet.decimalDigitCharacterSet())

    if validChars.isSupersetOfSet(NSCharacterSet(charactersInString: string)){
        switch string.componentsSeparatedByString(decimalSeparator).count-1 {
        case 0: //no decimals
            return true

        case 1: //if adding decimal, only allow if no existing decimal
            if let existingText = textField.text{
                return existingText.componentsSeparatedByString(decimalSeparator).count <= 1
            }
            else {return true}

        default: //invalid decimals
            return false
        }
    }

    return false
}
1
David

Ce qui suit est le code que j'ai utilisé dans Swift 3.0 et adapté du code de M. H. Les différences sont dues au fait que:

a) La déclaration de la fonction de délégué a été modifiée dans Swift 3.0. Nouvelle déclaration ici

b) La déclaration NSCharacterSet a été modifiée.

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

        let inverseSet = NSCharacterSet(charactersIn:"0123456789").inverted

        let components = string.components(separatedBy: inverseSet)

        let filtered = components.joined(separator: "")

        return string == filtered

}
1
ChinLoong

Je pense que vous pouvez forcer le type de clavier à changer en implémentant le protocole UITextInputTraits

//class ViewController: UIViewController, UITextInputTraits {

@IBOutlet weak var textFieldKeyboardType: UITextField!{
    didSet{
        textFieldKeyboardType.keyboardType = UIKeyboardType.NumberPad
    }
}
var keyboardType: UIKeyboardType { 
    get{ 
        return textFieldKeyboardType.keyboardType
    } 
    set{ 
        if newValue != UIKeyboardType.NumberPad{
            self.keyboardType = UIKeyboardType.NumberPad
        }
    } 
}
1
Lukas

Solution simple et mortelle pour les numéros Double (gardez à l'esprit que ce n'est pas la meilleure solution conviviale), dans votre délégué UITextFieldDelegate:

func textField(_ textField: UITextField,
               shouldChangeCharactersIn range: NSRange,
               replacementString string: String) -> Bool {
        guard let currentString = textField.text as NSString? else {
            return false
        }
        let newString = currentString.replacingCharacters(in: range, with: string)
        return Double(newString) != nil
}
0
Tomasz Wójcik
Swift 2.0

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

        let inverseSet = NSCharacterSet(charactersInString:"0123456789").invertedSet

        let components = string.componentsSeparatedByCharactersInSet(inverseSet)

        let filtered = components.joinWithSeparator("")

        return string == filtered

  }
0
tymac

Ceci est une version plus lisible qui fera "0-9" plus ".":

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

    let existingTextHasDecimal = textField.text?.rangeOfString(".")
    let replacementTextHasDecimal = string.rangeOfString(".")
    let replacementTextAllCharacters = NSCharacterSet(charactersInString: string)
    let replacementTextOnlyDigits = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(replacementTextAllCharacters)

    if replacementTextHasDecimal != nil && existingTextHasDecimal != nil {
        return false
    }else{
        if replacementTextOnlyDigits == true {
            return true
        }else if replacementTextHasDecimal != nil{
            return true
        }else{
            return false
        }
    }
}
0
Enraged
func isValidNumber(str:String) -> Bool{
    if str.isEmpty {
        return false
    }
    let newChar = NSCharacterSet(charactersInString: str)
    let boolValid = NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(newChar)
    if boolValid{
        return true
    }else{
        let lst = str.componentsSeparatedByString(".")
        let newStr = lst.joinWithSeparator("")
        let currentChar = NSCharacterSet(charactersInString: newStr)
        if lst.count == 2 && !lst.contains("") && NSCharacterSet.decimalDigitCharacterSet().isSupersetOfSet(currentChar){
            return true
        }
        return false
    }
}

Mettez cette fonction dans votre méthode "Submit" ou "Save" s'il y en a une.

0
den330

Swift 3 

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if textField==yourTextFieldOutlet {
                if(CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: yourTextFieldOutlet.text!))){
//if numbers only, then your code here
                }
                else{
                showAlert(title: "Error",message: "Enter Number only",type: "failure")
                }
            }
    return true
    }
0
ArgaPK

Vous pouvez utiliser ce code si vous souhaitez autoriser le séparateur décimal et/ou les nombres négatifs . Mais ce code autorise l'exemple: "34." (séparateur décimal à la fin) lors de la modification du texte. Vous devez donc ajouter un exemple de code: textFieldShouldReturn ou textFieldShouldEndEditing des fonctions de délégué.

Le code écrit dans Swift 4 mais je suppose que cela est compatible avec Swift 3.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    guard let text = textField.text else {
        return true
    }

    let replaced = (text as NSString).replacingCharacters(in: range, with: string)
    let decimalSeparator = NSLocale.current.decimalSeparator ?? ""

    // When user wants to delete las character
    if replaced == "" || replaced == "-" || replaced == "-0" {
        textField.text = "0"
        return false
    }

    // When text contains 0 before replace except "0."
    if replaced != "0" + decimalSeparator && replaced.hasPrefix("0") && text.underestimatedCount == 1 {
        textField.text = replaced.substring(from: replaced.index(after: replaced.startIndex))
        return false
    }

    // When user wants to delete minus sign
    if text.hasPrefix("-") && text.substring(from: text.index(after: text.startIndex)) == replaced {
        return false
    }

    // When user wants to delete before decimal separator
    if replaced.hasPrefix(decimalSeparator) || replaced.hasPrefix("-" + decimalSeparator) {
        return false
    }

    // When user wants to add zero the beginning of number... but allowing "0." or "-0." numbers
    let testReplaced = replaced.hasPrefix("-") ? replaced.substring(from: replaced.index(after: replaced.startIndex)) : replaced
    if testReplaced.count >= 2 && testReplaced.hasPrefix("0") && !testReplaced.hasPrefix("0" + decimalSeparator) {
        return false
    }

    // Every other cases
    let allowDecimal = self.allowFloat ? (decimalSeparator == "." ? "\\.?" : decimalSeparator + "?") : ""
    let allowSign = self.allowSigned ? "-?" : ""
    let pattern = "\(allowSign)[0-9]+\(allowDecimal)([0-9]+)?"

    do {
        let regexRange = (replaced as NSString).range(of: replaced)
        let regex = try NSRegularExpression(pattern: pattern, options: [])
        let matches = regex.matches(in: replaced, options: [], range: regexRange)
        return matches.count == 1 && matches.first!.range == regexRange
    }
    catch {}

    return false
}

Si vous ne souhaitez pas autoriser les nombres décimaux ou négatifs, vous devez remplacer la variable de remorquage par la ligne suivante.

let allowDecimal = ""
let allowSign = ""
0
Ferenc Kiss

Vous pouvez utiliser shouldChangeCharactersInRange avec la méthode d'extension String pour vérifier si la chaîne d'entrée est numérique ou non. 

extension String  {

    var isNumber : Bool {
        get{
            return !self.isEmpty && self.stringWithoutWhitespaces.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil
        }
    }

    var stringWithoutWhitespaces: String {
        return self.replacingOccurrences(of: " ", with: "")
    }

}

//Mark: shouldChangeCharactersInRange
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    // return true if the string only contains numeric characters
    let isValid = string.stringWithoutWhitespaces.isNumber 
    return valid
}
0
Suhit Patil

Voici une solution plus propre: 

 guard CharacterSet(charactersIn: "123456789").isSuperset(of: CharacterSet(charactersIn: string)) else {
     return false
 }
 return true

Pour les nombres décimaux, ajoutez simplement ., exemple 123456789.

0
Farhad