web-dev-qa-db-fra.com

Vérifier qu'une entrée dans UITextField est uniquement numérique

Comment valider l'entrée de chaîne sur un UITextField? Je veux vérifier que la chaîne est numérique, points décimaux compris.

82
g.revolution

J'utilise ce code dans mon application Mac, le même ou similaire devrait fonctionner avec l'iPhone. Il est basé sur les expressions régulières RegexKitLite et rend le texte rouge quand il est invalide.

static bool TextIsValidValue( NSString* newText, double &value )
{
    bool result = false;

    if ( [newText isMatchedByRegex:@"^(?:|0|[1-9]\\d*)(?:\\.\\d*)?$"] ) {
        result = true;
        value = [newText doubleValue];
    }
    return result;
}

- (IBAction) doTextChanged:(id)sender;
{
    double value;
    if ( TextIsValidValue( [i_pause stringValue], value ) ) {
        [i_pause setTextColor:[NSColor blackColor]];
        // do something with the value
    } else {
        [i_pause setTextColor:[NSColor redColor]];
    }
}
34
Peter N Lewis

Vous pouvez le faire en quelques lignes comme ceci:

BOOL valid;
NSCharacterSet *alphaNums = [NSCharacterSet decimalDigitCharacterSet];
NSCharacterSet *inStringSet = [NSCharacterSet characterSetWithCharactersInString:myInputField.text];
valid = [alphaNums isSupersetOfSet:inStringSet];    
if (!valid) // Not numeric

- c'est pour valider que la saisie est uniquement numérique. Regardez la documentation pour NSCharacterSet pour les autres options. Vous pouvez utiliser characterSetWithCharactersInString pour spécifier un ensemble de caractères d'entrée valides.

115

Vous pouvez le faire de différentes manières:

  1. Utilisez la méthode numberFromString: de NSNumberFormatter. Cela retournera un NSNumber s'il peut analyser correctement la chaîne, ou nil s'il ne le peut pas.
  2. Utiliser NSScanner
  3. Dénudez tous les caractères non numériques et voyez si la chaîne correspond toujours
  4. Utilisez une expression régulière

OMI, en utilisant quelque chose comme -[NSString doubleValue] _ ne serait pas la meilleure option car les deux @"0.0" et @"abc" aura une valeur doubleValue sur 0. Les méthodes * value renvoient toutes 0 si elles ne sont pas en mesure de convertir la chaîne correctement. Il serait donc difficile de distinguer une chaîne légitime de @"0" et une chaîne non valide. Quelque chose comme la fonction strtol de C aurait le même problème.

Je pense que NSNumberFormatter serait la meilleure option, car elle prend en compte les paramètres régionaux (c.-à-d. Le nombre @"1,23" en Europe, versus @"1.23" aux Etats-Unis).

72
Dave DeLong

Si vous souhaitez qu'un utilisateur ne soit autorisé qu'à entrer des chiffres, vous pouvez demander à votre ViewController d'implémenter une partie de UITextFieldDelegate et définir cette méthode:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
  NSString *resultingString = [textField.text stringByReplacingCharactersInRange: range withString: string];

  // The user deleting all input is perfectly acceptable.
  if ([resultingString length] == 0) {
    return true;
  }

  NSInteger holder;

  NSScanner *scan = [NSScanner scannerWithString: resultingString];

  return [scan scanInteger: &holder] && [scan isAtEnd];
}

Il existe probablement des moyens plus efficaces, mais je trouve cela très joli . Et la méthode devrait être facilement adaptable à la validation des doublons ou peu importe: utilisez simplement scanDouble: ou similaire.

19
Frank Shearar
#pragma mark - UItextfield Delegate

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    if ([string isEqualToString:@"("]||[string isEqualToString:@")"]) {
        return TRUE;
    }

    NSLog(@"Range ==%d  ,%d",range.length,range.location);
    //NSRange *CURRANGE = [NSString  rangeOfString:string];

    if (range.location == 0 && range.length == 0) {
        if ([string isEqualToString:@"+"]) {
            return TRUE;
        }
    }
    return [self isNumeric:string];
}

-(BOOL)isNumeric:(NSString*)inputString{
    BOOL isValid = NO;
    NSCharacterSet *alphaNumbersSet = [NSCharacterSet decimalDigitCharacterSet];
    NSCharacterSet *stringSet = [NSCharacterSet characterSetWithCharactersInString:inputString];
    isValid = [alphaNumbersSet isSupersetOfSet:stringSet];
    return isValid;
}
13
kalpesh jetani

Voici quelques lignes simples combinant la réponse de Peter Lewis ci-dessus ( Vérifiez qu'une entrée dans UITextField est uniquement numérique ) avec NSPredicates

    #define REGEX_FOR_NUMBERS   @"^([+-]?)(?:|0|[1-9]\\d*)(?:\\.\\d*)?$"
    #define REGEX_FOR_INTEGERS  @"^([+-]?)(?:|0|[1-9]\\d*)?$"
    #define IS_A_NUMBER(string) [[NSPredicate predicateWithFormat:@"SELF MATCHES %@", REGEX_FOR_NUMBERS] evaluateWithObject:string]
    #define IS_AN_INTEGER(string) [[NSPredicate predicateWithFormat:@"SELF MATCHES %@", REGEX_FOR_INTEGERS] evaluateWithObject:string]
11
Sai Ramachandran

Pour le test entier, ce sera:

- (BOOL) isIntegerNumber: (NSString*)input
{
    return [input integerValue] != 0 || [input isEqualToString:@"0"];
}
4
zvjerka24

Messieurs, le meilleur moyen d’atteindre votre objectif est d’afficher un clavier numérique plutôt que le clavier normal. Cela limite les clés disponibles pour l'utilisateur. Cela évite d'avoir à valider et, plus important encore, empêche l'utilisateur de commettre une erreur. Le pavé numérique est également beaucoup plus agréable pour la saisie de chiffres car les touches sont beaucoup plus grandes.

Dans le générateur d'interface, sélectionnez UITextField, accédez à l'inspecteur d'attributs et remplacez le "Type de clavier" par "Pavé décimal".

enter image description here

Cela fera ressembler le clavier à ceci:

enter image description here

Il ne reste plus qu'à s'assurer que l'utilisateur n'entre pas deux décimales. Vous pouvez le faire pendant leur édition. Ajoutez le code suivant à votre contrôleur de vue. Ce code supprime une deuxième décimale dès qu'il est entré. Il apparaît à l'utilisateur que la 2e décimale n'est jamais apparue.

- (void)viewDidLoad
{
  [super viewDidLoad];

  [self.textField addTarget:self
                    action:@selector(textFieldDidChange:)
           forControlEvents:UIControlEventEditingChanged];
}

- (void)textFieldDidChange:(UITextField *)textField
{
  NSString *text = textField.text;
  NSRange range = [text rangeOfString:@"."];

  if (range.location != NSNotFound &&
      [text hasSuffix:@"."] &&
      range.location != (text.length - 1))
  {
    // There's more than one decimal
    textField.text = [text substringToIndex:text.length - 1];
  }
}
3
David

Vous pouvez utiliser la double valeur de votre chaîne comme

NSString *string=@"1.22";
double a=[string doubleValue];

je pense que cela renverra un as de 0,0 si la chaîne est invalide (elle peut renvoyer une exception, auquel cas vous pouvez simplement l'attraper, la documentation dit 0.0 tho). plus d'infos ici

3
Daniel

En retard au jeu, mais voici une petite catégorie pratique que j’utilise qui prend en compte les décimales et le symbole local utilisé. lien vers son contenu ici

@interface NSString (Extension)

- (BOOL) isAnEmail;
- (BOOL) isNumeric;

@end

@implementation NSString (Extension)

/**
 *  Determines if the current string is a valid email address.
 *
 *  @return BOOL - True if the string is a valid email address.
 */

- (BOOL) isAnEmail
{
    NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
    NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];

    return [emailTest evaluateWithObject:self];
}

/**
 *  Determines if the current NSString is numeric or not. It also accounts for the localised (Germany for example use "," instead of ".") decimal point and includes these as a valid number.
 *
 *  @return BOOL - True if the string is numeric.
 */

- (BOOL) isNumeric
{
    NSString *localDecimalSymbol = [[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator];
    NSMutableCharacterSet *decimalCharacterSet = [NSMutableCharacterSet characterSetWithCharactersInString:localDecimalSymbol];
    [decimalCharacterSet formUnionWithCharacterSet:[NSCharacterSet alphanumericCharacterSet]];

    NSCharacterSet* nonNumbers = [decimalCharacterSet invertedSet];
    NSRange r = [self rangeOfCharacterFromSet: nonNumbers];

    if (r.location == NSNotFound)
    {
        // check to see how many times the decimal symbol appears in the string. It should only appear once for the number to be numeric.
        int numberOfOccurances = [[self componentsSeparatedByString:localDecimalSymbol] count]-1;
        return (numberOfOccurances > 1) ? NO : YES;
    }
    else return NO;
}

@end
3
bennythemink

Salut a eu exactement le même problème et je ne vois pas la réponse que j'ai utilisée, alors la voici.

J'ai créé et connecté mon champ de texte via IB. Lorsque je l'ai connecté à mon code via Ctrl + Drag, j'ai choisi Action, puis sélectionné l'événement Modification modifiée. Cela déclenche la méthode sur chaque entrée de caractère. Vous pouvez utiliser un autre événement en fonction.

Ensuite, j'ai utilisé ce code simple pour remplacer le texte. Notez que j'ai créé mon propre jeu de caractères pour inclure le caractère décimal/point et les nombres. Fondamentalement sépare la chaîne sur les caractères non valides, puis les rejoint avec une chaîne vide.

- (IBAction)myTextFieldEditingChangedMethod:(UITextField *)sender {
        NSCharacterSet *validCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@".0123456789"];
        NSCharacterSet *invalidCharacterSet = validCharacterSet.invertedSet;
        sender.text = [[sender.text componentsSeparatedByCharactersInSet:invalidCharacterSet] componentsJoinedByString:@""];
}

Crédits: Supprimer tout sauf les chiffres de NSString

3
guptron
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    if(string.length > 0)
    {
        NSCharacterSet *numbersOnly = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"];
        NSCharacterSet *characterSetFromTextField = [NSCharacterSet characterSetWithCharactersInString:string];

        BOOL stringIsValid = [numbersOnly isSupersetOfSet:characterSetFromTextField];
        return stringIsValid;
    }
    return YES;
}
3
Hsm

Ancien fil de discussion, mais il convient de mentionner que Apple a introduit NSRegularExpression dans iOS 4.0. (Prenant l'expression régulière de la réponse de Peter)

// Look for 0-n digits from start to finish
NSRegularExpression *noFunnyStuff = [NSRegularExpression regularExpressionWithPattern:@"^(?:|0|[1-9]\\d*)(?:\\.\\d*)?$" options:0 error:nil];

// There should be just one match
if ([noFunnyStuff numberOfMatchesInString:<#theString#> options:0 range:NSMakeRange(0, <#theString#>.length)] == 1)
{
    // Yay, digits!
}

Je suggère de stocker l'instance NSRegularExpression quelque part.

2
Can
@property (strong) NSNumberFormatter *numberFormatter;
@property (strong) NSString *oldStringValue;

- (void)awakeFromNib 
{
  [super awakeFromNib];
  self.numberFormatter = [[NSNumberFormatter alloc] init];
  self.oldStringValue = self.stringValue;
  [self setDelegate:self];
}

- (void)controlTextDidChange:(NSNotification *)obj
{
  NSNumber *number = [self.numberFormatter numberFromString:self.stringValue];
  if (number) {
    self.oldStringValue = self.stringValue;
  } else {
    self.stringValue = self.oldStringValue;
  }
}
2
StuFF mc

Je voulais un champ de texte qui ne permettait que des entiers. Voici ce que j'ai fini avec (en utilisant des informations d'ici et d'ailleurs):

Créez un formateur de nombre entier (dans UIApplicationDelegate pour pouvoir le réutiliser):

@property (nonatomic, retain) NSNumberFormatter *integerNumberFormatter;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Create and configure an NSNumberFormatter for integers
    integerNumberFormatter = [[NSNumberFormatter alloc] init];
    [integerNumberFormatter setMaximumFractionDigits:0];

    return YES;
}

Utilisez le filtre dans UITextFieldDelegate:

@interface MyTableViewController : UITableViewController <UITextFieldDelegate> {
    ictAppDelegate *appDelegate;
}

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    // Make sure the proposed string is a number
    NSNumberFormatter *inf = [appDelegate integerNumberFormatter];
    NSString* proposedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
    NSNumber *proposedNumber = [inf numberFromString:proposedString];
    if (proposedNumber) {
        // Make sure the proposed number is an integer
        NSString *integerString = [inf stringFromNumber:proposedNumber];
        if ([integerString isEqualToString:proposedString]) {
            // proposed string is an integer
            return YES;
        }
    }

    // Warn the user we're rejecting the change
    AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
    return NO;
}
1
Symmetric

Accepter les valeurs décimales dans les champs de texte avec un (.) Point fonctionnant avec iPad et iPhone 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
            }
        }
    }
1
Raj Joshi

Pas si élégant, mais simple :)

- (BOOL) isNumber: (NSString*)input
{
    return [input doubleValue] != 0 || [input isEqualToString:@"0"] || [input isEqualToString:@"0.0"];
}
1
zvjerka24
   #import "NSString+Extension.h"

//@interface NSString (Extension)
//
//- (BOOL) isAnEmail;
//- (BOOL) isNumeric;
//
//@end

@implementation NSString (Extension)
 - (BOOL) isNumeric
    {
        NSString *emailRegex = @"[0-9]+";
        NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];

        return [emailTest evaluateWithObject:self];

    //    NSString *localDecimalSymbol = [[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator];
    //    NSMutableCharacterSet *decimalCharacterSet = [NSMutableCharacterSet characterSetWithCharactersInString:localDecimalSymbol];
    //    [decimalCharacterSet formUnionWithCharacterSet:[NSCharacterSet alphanumericCharacterSet]];
    //    
    //    NSCharacterSet* nonNumbers = [decimalCharacterSet invertedSet];
    //    NSRange r = [self rangeOfCharacterFromSet: nonNumbers];
    //    
    //    if (r.location == NSNotFound)
    //    {
    //        // check to see how many times the decimal symbol appears in the string. It should only appear once for the number to be numeric.
    //        int numberOfOccurances = [[self componentsSeparatedByString:localDecimalSymbol] count]-1;
    //        return (numberOfOccurances > 1) ? NO : YES;
    //    }
    //    else return NO;
    }
0
Gank

Dans Swift 4:

let formatString = "12345"
if let number = Decimal(string:formatString){

    print("String contains only number")
}
else{
    print("String doesn't contains only number")
}
0
Ankit garg

Cela couvre: le contrôle des parties décimales (y compris le nombre de décimales autorisées), le contrôle du copier/coller, les séparateurs internationaux.

Pas:

  1. Assurez-vous que votre contrôleur de vue hérite de UITextFieldDelegate

    classe MyViewController: UIViewController, UITextFieldDelegate {...

  2. Dans viewDidLoad, définissez votre délégué de contrôle sur self:

    remplacer func viewDidLoad () {super.viewDidLoad (); yourTextField.delegate = self}

  3. Implémentez la méthode suivante et mettez à jour la constante "decsAllowed" avec le nombre de décimales souhaité ou sur 0 si vous souhaitez un nombre naturel.

Swift 4

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

    let decsAllowed: Int = 2
    let candidateText = NSString(string: textField.text!).replacingCharacters(in: range, with: string)
    let decSeparator: String = NumberFormatter().decimalSeparator!;

    let splitted = candidateText.components(separatedBy: decSeparator)
    let decSeparatorsFound = splitted.count - 1
    let decimalPart = decSeparatorsFound > 0 ? splitted.last! : ""
    let decimalPartCount = decimalPart.characters.count

    let characterSet = NSMutableCharacterSet.decimalDigit()
    if decsAllowed > 0 {characterSet.addCharacters(in: decSeparator)}

    let valid = characterSet.isSuperset(of: CharacterSet(charactersIn: candidateText)) &&
                decSeparatorsFound <= 1 &&
                decsAllowed >= decimalPartCount

    return valid
}

Si, par la suite, vous devez convertir cette chaîne en un nombre en toute sécurité, vous pouvez simplement utiliser le transtypage Double (yourstring) ou Int (yourstring), ou la méthode la plus académique:

let formatter = NumberFormatter()
let theNumber: NSNumber = formatter.number(from: yourTextField.text)!
0
Jorge Ramírez

Comme indiqué précédemment, cette réponse utilise NSFormatter. Vérifiez-le:

@interface NSString (NSNumber)
- (BOOL) isNumberWithLocale:(NSLocale *) stringLocale;  
- (BOOL) isNumber;
- (NSNumber *) getNumber; 
- (NSNumber *) getNumberWithLocale:(NSLocale*) stringLocale;
@end

@implementation NSString (NSNumber)
- (BOOL) isNumberWithLocale:(NSLocale *) stringLocale
{
    return [self getNumberWithLocale:stringLocale] != nil;
}
- (BOOL) isNumber
{
    return [ self getNumber ] != nil;
}
- (NSNumber *) getNumber
{
    NSLocale *l_en = [[NSLocale alloc] initWithLocaleIdentifier: @"en_US"] ;  
    return [self getNumberWithLocale: [l_en autorelease] ];
}

- (NSNumber *) getNumberWithLocale:(NSLocale*) stringLocale
{
    NSNumberFormatter *formatter = [[ [ NSNumberFormatter alloc ] init ] autorelease];
    [formatter setLocale: stringLocale ];
    return [ formatter numberFromString:self ]; 
}
@end

J'espère que ça aide quelqu'un. =)

0
Leandro

Pour être plus international (et pas seulement américain ;-)), remplacez simplement le code ci-dessus par

-(NSNumber *) getNumber
{
  NSString* localeIdentifier = [[NSLocale autoupdatingCurrentLocale] localeIdentifier];
  NSLocale *l_en = [[NSLocale alloc] initWithLocaleIdentifier: localeIdentifier] ;
  return [self getNumberWithLocale: [l_en autorelease] ];
}
0
user1458963