Permitir solo números para la entrada UITextField

82

El iPad no tiene un teclado "Numpad" como el iPhone / iPod.

Estoy buscando cómo puedo restringir el teclado del usuario para que solo acepte valores del 0 al 9.

Me imagino usando "shouldChangeCharactersInRange" de UITextField pero no sé la mejor manera de implementarlo.

Demasterpl
fuente
Hice un tutorial sobre cómo lograr esto con el código fuente del proyecto descargable. Aquí: xcodenoobies.blogspot.com/2013/12/…
GeneCode
Vaya a través del enlace stackoverflow.com/questions/10734959/…
Himanshu padia

Respuestas:

86

Así es como puede manejar el problema en un campo de verificación SSN, puede modificar la longitud máxima y eliminar la ifdeclaración que verifica el tipo de teclado si es necesario.

También existe una lógica para suprimir las alertas de longitud máxima cuando el usuario está escribiendo en lugar de pegar datos.

Dentro del contexto de este código, presentAlert()/presentAlert:es solo una función básica que presenta un UIAlertController(o un legado UIAlertView) usando la cadena de mensaje pasada.

Rápido 5

// NOTE: This code assumes you have set the UITextField(s)'s delegate property to the 
// object that will contain this code, because otherwise it would never be called.
//
// There are also some better stylistic approaches in Swift to avoid all the 
// nested statements, but I wanted to keep the styles similar to allow others 
// to contrast and compare between the two languages a little easier.

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

    // Handle backspace/delete
    guard !string.isEmpty else {

        // Backspace detected, allow text change, no need to process the text any further
        return true
    }

    // Input Validation
    // Prevent invalid character input, if keyboard is numberpad
    if textField.keyboardType == .numberPad {

        // Check for invalid input characters
        if CharacterSet(charactersIn: "0123456789").isSuperset(of: CharacterSet(charactersIn: string)) {

            // Present alert so the user knows what went wrong
            presentAlert("This field accepts only numeric entries.")

            // Invalid characters detected, disallow text change
            return false
        }
    }

    // Length Processing
    // Need to convert the NSRange to a Swift-appropriate type
    if let text = textField.text, let range = Range(range, in: text) {

        let proposedText = text.replacingCharacters(in: range, with: string)

        // Check proposed text length does not exceed max character count
        guard proposedText.count <= maxCharacters else {

            // Present alert if pasting text
            // easy: pasted data has a length greater than 1; who copy/pastes one character?
            if string.count > 1 {

                // Pasting text, present alert so the user knows what went wrong
                presentAlert("Paste failed: Maximum character count exceeded.")
            }

            // Character count exceeded, disallow text change
            return false
        }

        // Only enable the OK/submit button if they have entered all numbers for the last four
        // of their SSN (prevents early submissions/trips to authentication server, etc)
        answerButton.isEnabled = (proposedText.count == 4)
    }

    // Allow text change
    return true
}

C objetivo

// NOTE: This code assumes you have set the UITextField(s)'s delegate property to the 
// object that will contain this code, because otherwise it would never be called.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    // Handle backspace/delete
    if (!string.length)
    {
        // Backspace detected, allow text change, no need to process the text any further
        return YES;
    }

    // Input Validation
    // Prevent invalid character input, if keyboard is numberpad
    if (textField.keyboardType == UIKeyboardTypeNumberPad)
    {
        if ([string rangeOfCharacterFromSet:[NSCharacterSet decimalDigitCharacterSet].invertedSet].location != NSNotFound)
        {
            [self presentAlert: @"This field accepts only numeric entries."];
            return NO;
        }
    }

    // Length Validation
    NSString *proposedText = [textField.text stringByReplacingCharactersInRange:range withString:string];

    // Check proposed text length does not exceed max character count
    if (proposedText.length > maxCharacters)
    {
        // Present alert if pasting text
        // easy: pasted data has a length greater than 1; who copy/pastes one character?
        if (string.length > 1)
        {
            // Pasting text, present alert so the user knows what went wrong
            [self presentAlert: @"Paste failed: Maximum character count exceeded."];
        }

        // Character count exceeded, disallow text change
        return NO;
    }

    // Only enable the OK/submit button if they have entered all numbers for the last four
    // of their SSN (prevents early submissions/trips to authentication server, etc)
    self.answerButton.enabled = (proposedText.length == maxCharacters);

    // Allow text change
    return YES;
}
Beltalowda
fuente
2
¡Gracias! ¡La sección 'eliminar caracteres no válidos de la entrada, si el teclado es un teclado numérico' ayudó a responder mi pregunta!
Demasterpl
@Gargo la pregunta establece específicamente que los únicos valores permitidos deben ser los dígitos del 0 al 9. El carácter de punto no cae dentro de esos requisitos. Para permitir el carácter de período, uno puede ver la respuesta dada por Aje aquí .
Beltalowda
ya lo usé pero deja el problema con ceros a la izquierda
Gargo
@Gargo puede usar algo similar a lo que tiene para detectar otros caracteres de punto y solo devolver sí para un carácter cero si: el campo de texto está actualmente vacío, si el punto de inserción está en el índice 0 y el siguiente carácter es un punto, o si el punto de inserción está en un índice mayor que el de un carácter de punto existente. Al menos esa sería una forma en que podría verificar para asegurarme de que el cero ingresado no creará un problema de cero a la izquierda.
Beltalowda
26

Puede usar este código para permitir solo números en textField.

Antes de que establezca delegado para textField

      textFieldName.delegate=self;

o

      [textFieldName setDelegate:self];

Luego use este código para permitir solo dígitos para textField

      - (BOOL) textField: (UITextField *)theTextField shouldChangeCharactersInRange:(NSRange)range replacementString: (NSString *)string {
//return yes or no after comparing the characters

      // allow backspace
      if (!string.length)
      {
           return YES;
      }

      ////for Decimal value start//////This code use use for allowing single decimal value
      //    if ([theTextField.text rangeOfString:@"."].location == NSNotFound)
      //    {
      //        if ([string isEqualToString:@"."]) {
      //            return YES;
      //        }
      //    }
      //    else
      //    {
      //        if ([[theTextField.text substringFromIndex:[theTextField.text rangeOfString:@"."].location] length]>2)   // this allow 2 digit after decimal 
      //        {
      //            return NO;
      //        }
      //    }
      ////for Decimal value End//////This code use use for allowing single decimal value

      // allow digit 0 to 9
      if ([string intValue])
      {
            return YES;
      }

      return NO;
    }
Aje
fuente
5
por cierto, para otros que usan este código, [string intValue]devuelve 0 para @ "0", por if ([string intValue])lo que no se cumple para @ "0". Mejor usoif ([string rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]].location != NSNotFound)
CharlesA
2
@".".intValuees 0. Y también @"0".intValuees 0.
Jaybo
Para aclarar los otros comentarios aquí: Este código no permite que el usuario escriba un carácter cero ( 0).
Beltalowda
23

Intente esto para evitar problemas de limpieza del campo de texto

Swift 3.0

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    guard NSCharacterSet(charactersInString: "0123456789").isSupersetOfSet(NSCharacterSet(charactersInString: string)) else {
        return false
    }
    return true
}

Swift 4.0

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    guard CharacterSet(charactersIn: "0123456789").isSuperset(of: CharacterSet(charactersIn: string)) else {
        return false
    }
    return true
}
SPatel
fuente
2
Puede simplificar el método de delegado y simplemente a la izquierdareturn guard CharacterSet(charactersIn: "0123456789").isSuperset(of: CharacterSet(charactersIn: string))
hamsternik
Copié y pegué en mi código y no funciona. ¿Cómo lo conecto y hago que funcione?
Yash Jain
primero configure textField delegate (textField.delegate = self) y conforme al protocolo UITextFieldDelegate eso es todo.
SPatel
19

Pasos muy específicos para el código Swift

Puede proporcionar lógica que restrinja la entrada del campo de texto en el func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Boolmétodo implementando el UITextFieldDelegateprotocolo.

En aras de la claridad, estos pasos asumen que su guión gráfico contiene un controlador de vista con un objeto de campo de texto que solo debe aceptar dígitos.

  1. Cree una clase personalizada para el controlador de vista que se extiende UIViewController. Asegúrese de que la escena en su guión gráfico se refiera a la clase personalizada estableciendo el valor de la clase personalizada en el Inspector de identidad de Xcode.

    import UIKit
    class YourCustomController: UIViewController {
        override func viewDidLoad() {        
            super.viewDidLoad()
        }
    }
    
  2. Cree una salida desde el campo de texto de su escena a su controlador de vista personalizado.

    class YourCustomController: UIViewController {
        @IBOutlet weak var numberField: UITextField!
        ...
    }
    
  3. Aplica el UITextFieldDelegateprotocolo en tu controlador de vista personalizado.

    class YourCustomController: UIViewController, UITextFieldDelegate {
        ...
    }
    
  4. En el viewDidLoadmétodo de su controlador de vista personalizado , asigne el delegado de su campo de texto a su clase de controlador de vista personalizado.

    override func viewDidLoad() {        
        super.viewDidLoad()
        numberField.delegate = self
    }
    
  5. Añadir la UITextFieldDelegate's func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Boolmétodo.

    Como resultado de hacer que su controlador de vista personalizado sea el numberFielddelegado en el paso anterior, este método se llamará cada vez que un usuario ingrese un carácter en el campo de texto. Si su método regresa true, el carácter permanecerá en el campo de texto. Si su método regresa false, el carácter no permanecerá en el campo de texto.

    El stringparámetro es el carácter que ingresa el usuario. Si el stringcarácter se puede convertir en un, Intentonces está entre 0 y 9; de lo contrario, es un carácter no numérico.

    class YourCustomController: UIViewController, UITextFieldDelegate {
        ...
        func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    
            return Int(string) != nil
        }
    }
    

(Consulte a continuación el código del controlador de vista completa).


Ejemplo de controlador de vista con campo de texto de solo dígitos

import UIKit

class YourCustomController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var numberField: UITextField!

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

    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {        
        return Int(string) != nil
    }    
}

Ejemplo de controlador de vista con un campo de texto decimal

Si desea admitir un número decimal, aproveche NSNumberFormatter. Consulte los comentarios del código para conocer las diferencias.

import UIKit

class YourCustomController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var numberField: UITextField!

    private var formatter: NSNumberFormatter!

    override func viewDidLoad() {        
        super.viewDidLoad()       
        numberField.delegate = self

        // Initialize the formatter; minimum value is set to zero; style is Decimal. 
        formatter = NSNumberFormatter()
        formatter.numberStyle = NSNumberFormatterStyle.DecimalStyle
        formatter.minimum = 0
    }

    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        // Combine the current text field value and the new string
        // character. If it conforms to the formatter's settings then
        // it is valid. If it doesn't then nil is returned and the
        // string character should not be allowed in the text field.         
        return formatter.numberFromString("\(textField.text)\(string)") != nil
    }    
}
blanco
fuente
2
Esto es bueno, pero hizo un pequeño ajuste ya que no le permite eliminar nada en el campo sin una verificación de cadena vacía. También agregué la capacidad de negativo al verificar el primer carácter si (cadena == "-" && range.location == 0) || string == "" {return true} return string.toInt ()! = nil
ickydime
return string.toInt() != nil Trabajado como un encanto. ¡Gracias!
CalZone
Tenga en cuenta que en Swift 2 tuve que cambiar esto areturn Int(string) != nil
nevster
@nevster ¡Gracias por el comentario! Creo que la mayoría de los desarrolladores de Swift se han movido o pasarán a Swift 2 y superior. Por lo tanto, actualicé la respuesta para que se ajuste a la conversión de cadena a int de Swift 2.
whyceewhite
7
Un cambio más que tuve que hacer: ¡la tecla de eliminación ya no funcionaba! Así que lo cambié areturn string == "" || Int(string) != nil
nevster
9
- (BOOL) textField: (UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString: (NSString *)string {

    NSNumberFormatter * nf = [[NSNumberFormatter alloc] init];
    [nf setNumberStyle:NSNumberFormatterNoStyle];

    NSString * newString = [NSString stringWithFormat:@"%@%@",textField.text,string];
    NSNumber * number = [nf numberFromString:newString];

    if (number)
        return YES;
    else
       return NO;
}
Blazej SLEBODA
fuente
1
Esto funciona bien para la fracción juste necesita cambiar la newString correcta: NSString * newString = [textField.text stringByReplacingCharactersInRange: range withString: string];
Idali
7

¡Apliqué esto y funciona!

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
// Check for non-numeric characters
NSUInteger lengthOfString = string.length;
for (NSInteger index = 0; index < lengthOfString; index++) {
    unichar character = [string characterAtIndex:index];
    if (character < 48) return NO; // 48 unichar for 0
    if (character > 57) return NO; // 57 unichar for 9
}
// Check total length for restrict user
NSUInteger proposedNewLength = textField.text.length - range.length + string.length;
if (proposedNewLength > 6)
    return YES;
return YES;                                                                                                                                     
}
iDev
fuente
1
Para agregar ".", Reemplácelo if (character < 48) return NO; // 48 unichar for 0 if (character > 57) return NO; // 57 unichar for 9con. if ((character < 48 || character > 57) && character != 46)También le recomendaría que compare charactercon representaciones hexadecimales de los números, ya que los hexadecimales se usan más comúnmente en estas circunstancias. Es decirif ((character < 0x30 || character > 0x39) && character != 0x2E)
Jacob R
2
NSString* val = [[textField text] stringByReplacingCharactersInRange:range withString:string];
    NSCharacterSet *allowedCharacterSet = [NSCharacterSet decimalDigitCharacterSet];
    if ([[string componentsSeparatedByCharactersInSet:[allowedCharacterSet invertedSet]] count] > 1 || [val length] > 5) {
        return NO;
    }
Johnny
fuente
2
Works fine for me :

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
if (([string rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]].location != NSNotFound) && !(range.length==1 && string.length==0)) {
            return NO;
        }
        return YES;
    }
Ankit Kumar Gupta
fuente
1

En Swift:

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        return string.isEmpty || Int(string) != nil
    }
mishimay
fuente
1

rápido 5

    //MARK:- UITextFieldDelegate

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

    let allowedCharacters = "1234567890"
    let allowedCharcterSet = CharacterSet(charactersIn: allowedCharacters)
    let typedCharcterSet = CharacterSet(charactersIn: string)
    return allowedCharcterSet.isSuperset(of: typedCharcterSet)
}

Ahora puede tocar solo 1234567890

Younes Idrissi
fuente
¿Cómo implementas esto? solo crear esta función no la conectará a UITextfield
Yash Jain
0

Mantenga distintos datos de presentación de la representación interna. Existe una forma más sencilla. Deja NSNumberFormatterhacer el trabajo:

 NSNumberFormatter* ns = [[NSNumberFormatter alloc] init];
 ns.numberStyle = NSNumberFormatterDecimalStyle;
 [ns setMaximumFractionDigits:2];
 // This is your internal representation of the localized number
 double a = [[ns numberFromString:self.textIVA.text] doubleValue]];

[mylabel setText:[NSString stringWithFormat:@"€ %@",
     [NSNumberFormatter localizedStringFromNumber:
                          [NSNumber numberWithDouble:a]
                                      numberStyle:NSNumberFormatterDecimalStyle]]];
Giuseppe
fuente
0

Si usa mi patrón de especificación , el código se ve así

textField.delegate = self

lazy var specification: Specification = {
    return RegularExpressionSpecification(pattern: "^(|0|[1-9]\\d{0,6})$")
}()

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let textFieldString: NSString = textField.text ?? ""
    let s = textFieldString.stringByReplacingCharactersInRange(range, withString:string)
    return specification.isSatisfiedBy(s)
}

func textFieldShouldReturn(textField: UITextField) -> Bool {
    let s = textField.text ?? ""
    let isTextValid = specification.isSatisfiedBy(s)
    if isTextValid {
        textField.resignFirstResponder()
    }
    return false
}
neoneye
fuente
¿Cómo restringiría el UITextfield para recibir solo números y limitar la cantidad de números entre 6 y 8?
Marco Almeida
Hola @MarcoAlmeida, echa un vistazo a mi marco SwiftyFORM, puede validar texto en vivo, github.com/neoneye/SwiftyFORM
neoneye
0

He modificado la respuesta de @ iDev para que funcione con digitales y ".":

-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
     // Check for non-numeric characters
     NSUInteger lengthOfString = string.length;
     for (NSInteger index = 0; index < lengthOfString; index++) {
         unichar character = [string characterAtIndex:index];
         if ((character < 48) && (character != 46)) return NO; 
         // 48 unichar for 0, and 46 unichar for point
         if (character > 57) return NO; 
         // 57 unichar for 9
     }
     // Check for total length
     NSUInteger proposedNewLength = textField.text.length - range.length + string.length;
     if (proposedNewLength > 6)
         return YES;
     return YES; 
 }
Darius Miliauskas
fuente
0

rápido 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
    }
ArgaPK
fuente
-1

Utilice este código:

NSString* val = [[textField text] stringByReplacingCharactersInRange:range withString:string];
NSCharacterSet *allowedCharacterSet = [NSCharacterSet decimalDigitCharacterSet];
if ([[string componentsSeparatedByCharactersInSet:[allowedCharacterSet invertedSet]] count] > 1 || [val length] > 5) {
    return NO;
}
usuario7592089
fuente