Compruebe que una entrada en UITextField sea solo numérica


Respuestas:


35

Utilizo este código en mi aplicación de Mac, el mismo o similar debería funcionar con el iPhone. Se basa en las expresiones regulares de RegexKitLite y convierte el texto en rojo cuando no es válido.

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

7
Sí, esta es una respuesta horriblemente antigua, lo sé, pero pensé que los métodos estáticos se declararon así: + (BOOL) TextIsValidValue:(NSString*)newText:(double)&value:...
RCIX

7
TextIsValidValue es una función C estática, lo que significa que es local al archivo y no forma parte de ningún objeto. Usted (@RCIX) está describiendo un método de objeto, que es similar a un método estático en C ++ y es un concepto completamente diferente a una función C estática (¡o una variable estática para el caso!).
Peter N Lewis

La expresión coincide con la cadena vacía (que no es numérica).
Nikolai Ruhe

Generalmente, para hacer coincidir un número para la entrada, debe permitir la cadena vacía; de lo contrario, el usuario no puede, por ejemplo, escribir "1 <delete> 2".
Peter N Lewis

6
Para aquellos desarrolladores que no saben qué son las expresiones regulares o cómo funcionan, simplemente vaya a este decodificador de expresiones regulares wesbite y copie / pegue (?:|0|[1-9]\\d*)(?:\\.\\d*)para ver qué significa. También vea Regex Wikipedia
Honey

117

Puedes hacerlo en unas pocas líneas como esta:

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

- esto es para validar la entrada es solo caracteres numéricos. Consulte la documentación NSCharacterSetpara conocer las otras opciones. Puede utilizar characterSetWithCharactersInString para especificar cualquier conjunto de caracteres de entrada válidos.


22
esta respuesta no verifica dobles ni puntuación
zambono

72

Hay algunas formas de hacer esto:

  1. Utilice el método numberFromString: de NSNumberFormatter. Esto devolverá un NSNumber si puede analizar la cadena correctamente o nilsi no puede.
  2. Utilice NSScanner
  3. Quite cualquier carácter no numérico y vea si la cadena aún coincide
  4. Usa una expresión regular

En mi opinión, usar algo como -[NSString doubleValue]no sería la mejor opción porque ambos @"0.0"y @"abc"tendrán un valor doble de 0. Todos los métodos * value devuelven 0 si no pueden convertir la cadena correctamente, por lo que sería difícil distinguir entre un cadena legítima de @"0"y una cadena no válida. Algo como la strtolfunción de C tendría el mismo problema.

Creo que usar NSNumberFormatter sería la mejor opción, ya que tiene en cuenta la configuración regional (es decir, el número @"1,23"en Europa versus @"1.23"en los EE. UU.).


Hmmm, debo estar configurando las opciones incorrectamente para NSNumberFormatter. Cuando uso numberFromString usando la cadena "34jfkjdskj80", devolverá el número 3480. Parece que simplemente elimina los caracteres en lugar de devolver nil.
Tony Eichelberger

5
@ Tony, sí, no estoy seguro de lo que estás haciendo, porque los siguientes registros "N: (nulo)" para mí:NSNumberFormatter * f = [[NSNumberFormatter alloc] init]; NSNumber * n = [f numberFromString:@"34jfkjdskj80"]; NSLog(@"N: %@", n);
Dave DeLong

2
Esto es un poco antiguo, pero en caso de que alguien esté leyendo esto, pensé que debería valer la pena señalar que un NSScanner, como NSNumberFormatter, tiene en cuenta la configuración regional al analizar la cadena, siempre que lo use setLocale:en el objeto del escáner (podría, por ejemplo, proporcionar [NSLocale currentLocale]).
dreamlax

1
Algunas personas leen "Guerra y paz". Otros prefieren "Moby Dick". Yo mismo, creo que solo voy a extraer el conjunto de datos de Stack Overflow, filtrar las preguntas con las respuestas de Dave y simplemente disfrutar.
BP.

1
@MarkAmery Sí, puede confiar en ese comportamiento. Aunque puede que no esté documentado, así es como se ha comportado durante muchos años, y cambiarlo sería inviable, desde el punto de vista de la compatibilidad binaria.
Dave DeLong

19

Si desea que un usuario solo pueda ingresar números, puede hacer que su implementación ViewController sea parte de UITextFieldDelegate y defina este método:

- (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];
}

Probablemente haya formas más eficientes, pero me parece una forma bastante conveniente . Y el método debería ser fácilmente adaptable para validar dobles o lo que sea: simplemente use scanDouble: o similar.


13
#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;
}

11

Aquí hay algunas frases ingeniosas que combinan la respuesta de Peter Lewis anterior ( verifique que una entrada a UITextField sea solo numérica ) con 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]

Por favor, proporcione un enlace desde donde pueda obtener más información al respecto.
Smit Saraiya

4

Para la prueba de números enteros será:

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

Buena respuesta, pero aún así el problema con este enfoque sería tener el valor de entrada '232asdfsadf', considerado como Número.
Whoami

puede comparar la longitud de integerValue (volver a convertir a cadena) con la cadena original para asegurarse de que "123aoenuthr" no se considere un número entero.
Michael

3

Puedes usar el doubleValue de tu cadena como

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

Creo que esto devolverá un 0.0 si la cadena no es válida (podría generar una excepción, en cuyo caso puede simplemente detectarla, los documentos dicen 0.0 aunque). mas info aqui


Además, utilizar una función de biblioteca suele ser mejor que utilizar la propia. En este caso, otras culturas usan una coma para el punto decimal, y presumiblemente las bibliotecas Cocoa pueden manejar eso.
kibibu

sin mencionar que hacer comparaciones de igualdad de flotación literal es simplemente una mala práctica
M. Ryan

3

Hola, tuve exactamente el mismo problema y no veo la respuesta que usé publicada, así que aquí está.

Creé y conecté mi campo de texto a través de IB. Cuando lo conecté a mi código a través de Control + Arrastrar, elegí Acción, luego seleccioné el evento Edición modificada. Esto activa el método en cada entrada de carácter. Puede utilizar un evento diferente para adaptarse.

Luego, utilicé este código simple para reemplazar el texto. Tenga en cuenta que creé mi propio juego de caracteres para incluir el carácter decimal / punto y los números. Básicamente, separa la cadena de los caracteres no válidos y luego los vuelve a unir con una cadena vacía.

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

Créditos: elimine todos los números de NSString excepto los números


Números negativos (-)?
CMVR

3

Tarde en el juego, pero aquí utilizo una pequeña categoría práctica que representa los lugares decimales y el símbolo local que se usa para ello. enlace a su esencia aquí

@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

¿No debería ser el alfanuméricoCharacterSet decimalDigitCharacterSet en isNumeric? así es como lo uso.
Andrei Radulescu

No funciona, @ "A" obtiene el resultado es numérico SÍ, pero no es un número
Gank

3
- (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

En mi opinión, la mejor manera de lograr su objetivo es mostrar un teclado numérico en lugar del teclado normal. Esto restringe qué claves están disponibles para el usuario. Esto alivia la necesidad de realizar una validación y, lo que es más importante, evita que el usuario cometa un error. El teclado numérico también es mucho más agradable para ingresar números porque las teclas son sustancialmente más grandes.

En el generador de interfaz, seleccione UITextField, vaya al Inspector de atributos y cambie el "Tipo de teclado" a "Bloc de decimales".

ingrese la descripción de la imagen aquí

Eso hará que el teclado se vea así:

ingrese la descripción de la imagen aquí

Lo único que queda por hacer es asegurarse de que el usuario no ingrese dos decimales. Puede hacer esto mientras están editando. Agregue el siguiente código a su controlador de vista. Este código elimina un segundo decimal tan pronto como se ingresa. Al usuario le parece que el segundo decimal nunca apareció en primer lugar.

- (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];
  }
}

1
no se olvide de la capacidad de copiar y pegar - su enfoque falla en este caso
slxl

1
Esto no cubre todos los casos. Un usuario puede copiar y pegar texto o usar un teclado bluetooth.
frijol

2
@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

Hilo antiguo, pero vale la pena mencionar que Apple lo introdujo NSRegularExpressionen iOS 4.0. (Tomando la expresión regular de la respuesta 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!
}

Sugiero almacenar la NSRegularExpressioninstancia en algún lugar.


1

Quería un campo de texto que solo permitiera números enteros. Esto es lo que terminé con (usando información de aquí y de otros lugares):

Cree un formateador de números enteros (en UIApplicationDelegate para que pueda reutilizarse):

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

Utilice el filtro en 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

No tan elegante, pero simple :)

- (BOOL) isNumber: (NSString*)input
{
    return [input doubleValue] != 0 || [input isEqualToString:@"0"] || [input isEqualToString:@"0.0"];
}

1
Esta solución no funciona con ceros infinitos como 0.00. Así que no sugeriré que la gente lo use
Vinh

1

Acepte valores decimales en campos de texto con un solo punto (.) Al trabajar con iPad y iPhone en 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
            }
        }
    }

0

Para ser más internacional (y no solo de color estadounidense ;-)) simplemente reemplace en el código anterior por

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

0

Esta respuesta usa NSFormatter como se dijo anteriormente. Echale un vistazo:

@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

Espero que esto ayude a alguien. =)


0
   #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

En 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

Esto cubre: control de parte decimal (incluido el número de decimales permitidos), control de copiar / pegar, separadores internacionales.

Pasos:

  1. Asegúrese de que su controlador de vista herede de UITextFieldDelegate

    clase MyViewController: UIViewController, UITextFieldDelegate {...

  2. En viewDidLoad, configure su delegado de control en self:

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

  3. Implemente el siguiente método y actualice la constante "decsAllowed" con la cantidad deseada de decimales o 0 si desea un número natural.

Rápido 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 luego necesita convertir de manera segura esa cadena en un número, puede usar Double (yourstring) o Int (yourstring) type cast, o la forma más académica:

let formatter = NumberFormatter()
let theNumber: NSNumber = formatter.number(from: yourTextField.text)!
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.