2009-02-09 25 views
5

Estoy tratando de usar un NSTextField para la entrada del usuario entero. El campo de texto está vinculado a una propiedad NSNumber y en el método setter limpie el valor de entrada (asegúrese de que sea un int) y configure la propiedad si es necesario. Envío el willChangeValueForKey: y didChangeValueForKey :, pero la UI no se actualiza a los nuevos valores mientras ese campo de texto todavía está activo.vinculando NSTextField al NSNumber

Así, por ejemplo, puedo escribir "12abc" en el campo de texto que el método setter limpia hasta "12", pero el campo de texto todavía muestra "12abc".

Tengo el "Valor de actualización continua" marcado en el constructor de la interfaz.

(También he notado que el método de selección recibe una NSString, no un NSNumber. ¿Es eso normal?)

Cuál es la forma correcta de conectar un NSTextField a un NSNumber? ¿Cómo se ve el método setter para la propiedad? ¿Cómo evitar que los valores no numéricos aparezcan en el campo de texto?

Respuesta

0

Creo que necesita configurar un formateador de número para su NSTextField.

Lea sobre los formateadores en el sitio web de Apple: Applying Formatters.

+0

que añade un formateador número dejándolo caer en el campo de texto como se sugiere en el documento usted ha señalado, pero no pasa nada. Aún puedo escribir caracteres no numéricos. ¿Debo hacer algo más que conectar el formateador al campo de texto? – lajos

1

Como mencionó Ben, una forma de hacerlo es adjuntar un NSNumberFormatter a su campo de texto, lo cual es bastante trivial para configurar en el constructor de interfaces y probablemente sea Just Work ™.

Si no le gustan los diálogos modales que NSNumberFormatter arroja a sus usuarios cuando ingresan valores no numéricos, puede crear una subclase NSNumberFormatter para implementar un comportamiento de formato diferente, 'más tolerante'. Creo que reemplazando - numberFromString: quitar los caracteres no numéricos antes de llamar a la implementación de super debería hacer el truco.

Otro enfoque es crear y registrar su propia subclase NSValueTransformer para analizar cadenas en NSNumbers y viceversa, pero trataría de usar NSNumberFormatter primero ya que es claramente la clase que se diseñó para este propósito.

More about value transformers

10

I send the willChangeValueForKey: and didChangeValueForKey:, but the UI doesn't update to the new values while that text field is still active.

Hay muy pocas razones para enviar esos mensajes. Por lo general, puedes hacer el mismo trabajo mejor y más limpiamente implementando y usando accesadores (o, mejor aún, propiedades). KVO enviará las notificaciones cuando lo haga.

En su caso, desea rechazar o filtrar entradas falsas (como "12abc"). La herramienta correcta para esta tarea es la Validación de valores clave.

Para habilitar esto, marque la casilla "Validar inmediatamente" en el enlace en IB e implemente un método de validación.

Filtrado:

- (BOOL) validateMyValue:(inout NSString **)newValue error:(out NSError **)outError { 
    NSString *salvagedNumericPart; 
    //Determine whether you can salvage a numeric part from the string; in your example, that would be “12”, chopping off the “abc”. 
    *newValue = salvagedNumericPart; //@"12" 
    return (salvagedNumericPart != nil); 
} 

Rechazar:

- (BOOL) validateMyValue:(inout NSString **)newValue error:(out NSError **)outError { 
    BOOL isEntirelyNumeric; 
    //Determine whether the whole string (perhaps after stripping whitespace) is a number. If not, reject it outright. 
    if (isEntirelyNumeric) { 
     //The input was @"12", or it was @" 12 " or something and you stripped the whitespace from it, so *newValue is @"12". 
     return YES; 
    } else { 
     if (outError) { 
      *outError = [NSError errorWithDomain:NSCocoaErrorDomain code: NSKeyValueValidationError userInfo:nil]; 
     } 
     //Note: No need to set *newValue here. 
     return NO; 
    } 
} 

(I've also noticed that the setter method receives an NSString, not an NSNumber. Is that normal?)

Sí, a menos que utilice un transformador de valor que transforma las cadenas en números, conecte un formateador de número a la salida formatter, o un sustituto un NSNumber para NSString en su método de validación.

+0

Los métodos de validación parecen tener una firma de: - (BOOL) validateNewValue: (id *) error de ioValue: (NSError **) outError; Pero incluso si implemento un método con la firma correcta y devuelvo NO o corrijo el valor, el campo de texto todavía muestra caracteres no numéricos. – lajos

+0

Gracias por la nota sobre la firma del método: he corregido mi respuesta. ¿Marcó la casilla "Valida inmediatamente"? No estoy seguro de qué otra cosa podría ser el problema, sin ver el código. –

+0

Publiqué un proyecto de prueba aquí: www.codza.com/files/numericFieldTest.zip El método de validación devuelve correctamente SÍ o NO (ver registros), pero el campo de texto todavía muestra caracteres alfabéticos. Muchas gracias por mirar esto. Puede ponerse en contacto conmigo en www.codza.com/contact y también revisaré los comentarios aquí. – lajos

2

Hay un comentario importante sobre la excelente respuesta de Peter Hosey que me gustaría llevar al nivel superior (porque me lo perdí en mi primer pase).

Si desea validar/modificar el NSTextField cada vez que se ingresa un carácter, en lugar de solo cuando el usuario envía el campo, no puede obtener lo que desea de los enlaces solo. Debe asignar un delegado al campo de texto y luego implementar - (void)controlTextDidChange:(NSNotification *)aNotification en el delegado. Esto se llamará cada vez que el texto cambie. Si lo desea, puede llamar al validador de valor en controlTextDidChange.

Por ejemplo:

- (void)controlTextDidChange:(NSNotification *)aNotification 
{ 
    NSError *outError; 
    NSControl *textField = [aNotification object]; 
    NSString *myText = [textField stringValue]; 

    // myObject is the model object that owns the text in question 
    // the validator can modify myText by reference 
    [myObject validateValue:&myText error:&outError]]; 

    // update the NSNextField with the validated text 
    [postingObject setStringValue:myText]; 
}