2012-03-25 16 views
6

He subclase un UITextField y me gustaría utilizar un método similar a textField:shouldChangeCharactersInRange:replacementString: dentro de la subclase para recibir una notificación cuando se escribe un caracter y si es necesario bloquear el cambio, pero evitar establecer los campos se delegan a sí mismo.textField: shouldChangeCharactersInRange: replacementString: en la subclase

He encontrado si invalido keyboardInput:shouldInsertText:isMarkedText: y keyboardInputShouldDelete: Puedo obtener el efecto deseado, desafortunadamente estos métodos son privados y cualquier cosa que use la clase no pasará por el proceso de envío de la App Store.

¿Alguien sabe de un método público que logra lo mismo y no requiere que el campo sea su propio delegado?

ACTUALIZACIÓN:

Fui con la sugerencia de crear un objeto separado sólo para ser el delegado, que a su vez puede tener un delegado para reenviar mensajes a.

+0

Me podría aclarar? No está claro de inmediato por qué no usaría textField: shouldChangeCharactersInRange :, es decir, obtener la subclase para establecer sus instancias como sus propios delegados. – Obliquely

+1

El único problema con establecerlo como su propio delegado es reenviar eventos a un delegado real que podría querer responder a eventos de UITextField. Actualmente, estoy usando addTarget: action: forControlEvent: para responder a esos eventos internamente. –

Respuesta

2

Al haber intentado subclase antes de UITextField, he aprendido a evitar eso y sigo la ruta del delegado (tiene un método similar llamado - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string que debería hacer lo que creo que ha descrito).

+0

Claro, pero eso solo está disponible si configuro el campo de texto como su propio delegado, lo cual me gustaría evitar. –

+1

No me gusta hacer que el UITextField sea su propio delegado, generalmente utilizo mi propia subcategoría 'ScottTextFieldValidator' como la clase del delegado (que tiene todos los métodos/estado que necesita). No hay razón para que UITextField en sí mismo sea su propio delegado. –

+0

Sí, me estoy inclinando también hacia este enfoque. –

0

Usted puede utilizar el método delegado del UITextField,

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 
{ 

} 

Y no se olvide de configurar su textfield.delegate = self en viewDidLoad.

2

Una estrategia para explorar aquí es anular el método setDelegate: y luego reenviar algunos mensajes. Puede usar [super setDelegate: self] para asegurarse de que sus llamadas reciban los primeros mensajes en los mensajes de delegado. En su anulación de setDelegate: configure un ivar interno, p. Ej.

- (void) setDelegate: (id<UITextFieldDelegate>) internalDelegate; 
{ 
    [setInternalDelegate: internalDelegate]; 
} 

A continuación, para cada uno de los métodos de los delegados UITextField hacer lo suyo antes de reenviar el mensaje delegado, por ejemplo,

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; 
{ 
     // do your thing with range and replacement string 

     // now forward message on the 'other' delegate  
     [[self internalDelegate] textField: self shouldChangeCharactersInRange: range replacementString: string]; 
} 

Por lo general, tendrá que reemplazar todos los métodos de delegado, aunque la mayor parte de todo lo que hacen ellos es una recta hacia adelante.

Actualización En los comentarios, observa que el método de reenvío plantea problemas. Si es así, entonces la delegación tradicional es el camino a seguir. (Y, en general, es el camino a seguir, aunque utilicé el delegado de reenvío una o dos veces, no estoy seguro si, en retrospectiva, fue absolutamente esencial y no lo he comprobado para ver si Hemos puesto en práctica con UITextField. @Scott Corscadden tiene y no lo recomienda.)

el patrón más común es hacer que el ViewController que se ocupa de la vista en la que el UITextField es una subvista el delegado. No dice en su respuesta si hay una razón particular por la que necesita trabajar con la subclase. Si está empacando cosas interesantes en el UITextField, podría ser así, aunque siempre podría haber otro cartel que sugiera y cree una clase complementaria para UITextField que funcione y lo use como delegado. En cualquier caso, si es necesario, siempre puede obtener el objeto delegado para llamar a métodos adicionales en su subclase UITextField, p.

// in the delegate object class 

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string; 
{ 
     [delegate doSomeExtraThingInTheTextFieldSubclassThatItSeemsToMakeSenseToDoThereRatherThanHere]; 

     // maybe that's it, or maybe this object also wants to do something here... 
} 
+0

¿Hay alguna manera de reenviarlos sin tener que implementarlos? Entonces, ¿solo los métodos que utiliza mi subclase se llaman, pero todo lo demás se pasa al siguiente delegado? –

+1

No lo creo. Pero solo lleva cinco minutos implementar el reenvío para los 7 mensajes en el protocolo UITextFieldDelegate. Dado que es la interfaz de usuario y la gente es lenta en comparación con el envío de mensajes, la sobrecarga es irrelevante. – Obliquely

+1

Sería bueno reducir el código, sin embargo;) –

1

Puede jugar con Objective-C runtime para lograrlo.El siguiente código me funciona. La idea es:

  • en la subclase nos propusimos delegate propiedad a uno mismo;
  • reemplazamos el método setDelegate: en el que almacenamos el delegado suministrado a una variable (forwardDelegate);
  • anulando respondsToSelector:, forwardInvocation: y methodSignatureForSelector: llamamos al método en sí mismo o lo reenviamos al guardado forwardDelegate.

// MyTextField.h 
@interface MyTextField : UITextField 
@end 

// MyTextField.m 
@interface MyTextField() <UITextFieldDelegate> 
@end 

@implementation MyTextField { 
    id forwardDelegate; 
} 

- (void) myTextFieldCommonInit 
{ 
    [super setDelegate:self]; 
} 

- (id) initWithCoder:(NSCoder *)coder 
{ 
    if ((self = [super initWithCoder:coder])) { 
     [self myTextFieldCommonInit]; 
    } 
    return self; 
} 

- (id) initWithFrame:(CGRect)frame 
{ 
    if ((self = [super initWithFrame:frame])) { 
     [self myTextFieldCommonInit]; 
    } 

    return self; 
} 

- (void) setDelegate:(id)delegate 
{ 
    forwardDelegate = delegate; 
} 

- (BOOL) respondsToSelector:(SEL)selector 
{ 
    if ([super respondsToSelector:selector]) { 
     return YES; 
    } else { 
     return [forwardDelegate respondsToSelector:selector]; 
    } 
} 

- (void) forwardInvocation:(NSInvocation *)invocation 
{ 
    if ([super respondsToSelector:[invocation selector]]) { 
     [super forwardInvocation:invocation]; 
    } else if ([forwardDelegate respondsToSelector:[invocation selector]]) { 
     [invocation invokeWithTarget:forwardDelegate]; 
    } else { 
     [self doesNotRecognizeSelector:[invocation selector]]; 
    } 
} 

- (NSMethodSignature *) methodSignatureForSelector:(SEL)selector 
{ 
    NSMethodSignature *signature = [super methodSignatureForSelector:selector]; 
    if (signature) { 
     return signature; 
    } else { 
     return [forwardDelegate methodSignatureForSelector:selector]; 
    } 
} 

#pragma mark - UITextFieldDelegate 

- (BOOL) textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 
{ 
    ... 
} 

@end