iphone objective-c cocoa-touch uibutton key-value-observing

iphone - ¿Cómo escuchar el cambio de estado de UIButton?



objective-c cocoa-touch (4)

Muy bien, he descubierto una solución que funciona. Puedes escuchar la propiedad de texto de la etiqueta de título del botón.

[self.titleLabel addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

Parece que se dispara dos veces por cambio, por lo que debe verificar para asegurarse de que los valores de @ "antiguo" y @ "nuevo" en el diccionario de cambio pasado sean diferentes.

NOTA: No use @ "old" y @ "new" directamente. Las constantes son NSKeyValueChangeOldKey y NSKeyValueChangeNewKey respectivamente.

Estoy extendiendo UIButton con funcionalidad genérica para cambiar ciertos atributos de apariencia según el título que se muestra.

Para hacer esto, necesito detectar y responder a los cambios en la propiedad "estado". Esto es así, me aseguro de que la apariencia se ajuste correctamente si el usuario ha establecido diferentes títulos para diferentes estados. Supuse que necesitaría usar algún tipo de KVO como el siguiente:

[self addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionNew context:nil];

Pero esto no parece disparar el método observeValueForKeyPath: ... para @ "state" o @ "currentTitle". Supongo que esto se debe a que UIButton no implementa el patrón KVO para esas propiedades.

No quiero simplemente escuchar los clics. Esos eventos causan un cambio de estado, pero no son las únicas causas potenciales.

¿Alguien sabe una manera de escuchar y responder a los cambios de estado de un UIButton?

Gracias

ACTUALIZAR

Solo una nota ya que he aprendido algunas cosas en los últimos dos años;).

Desde entonces, he hablado con algunas personas de Apple que lo saben, y la razón por la que KVO no funciona en las propiedades del estado se debe al hecho de que NONE de UIKit garantiza que cumple con KVO. Pensé que valía la pena repetirlo aquí: si está intentando escuchar alguna propiedad de una clase de marco UIKit, tenga en cuenta que puede funcionar pero que no está oficialmente admitido y podría interrumpirse en diferentes versiones de iOS.


Necesitaba esto hoy, así que escribí esta clase que hace el trabajo:

MyButton.h

#import <UIKit/UIKit.h> // UIControlEventStateChanged uses the first bit from the UIControlEventApplicationReserved group #define UIControlEventStateChanged (1 << 24) @interface MyButton : UIButton @end

MyButton.m

#import "MyButton.h" #pragma mark - Private interface @interface MyButton () - (void)checkStateChangedAndSendActions; @end #pragma mark - Main class @implementation MyButton { // Prior state is used to compare the state before // and after calls that are likely to change the // state. It is an ivar rather than a local in each // method so that if one of the methods calls another, // the state-changed actions only get called once. UIControlState _priorState; } - (void)setEnabled:(BOOL)enabled { _priorState = self.state; [super setEnabled:enabled]; [self checkStateChangedAndSendActions]; } - (void)setSelected:(BOOL)selected { _priorState = self.state; [super setSelected:selected]; [self checkStateChangedAndSendActions]; } - (void)setHighlighted:(BOOL)highlighted { _priorState = self.state; [super setHighlighted:highlighted]; [self checkStateChangedAndSendActions]; } - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { _priorState = self.state; [super touchesBegan:touches withEvent:event]; [self checkStateChangedAndSendActions]; } - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { _priorState = self.state; [super touchesMoved:touches withEvent:event]; [self checkStateChangedAndSendActions]; } - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { _priorState = self.state; [super touchesEnded:touches withEvent:event]; [self checkStateChangedAndSendActions]; } - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { _priorState = self.state; [super touchesCancelled:touches withEvent:event]; [self checkStateChangedAndSendActions]; } #pragma mark - Private interface implementation - (void)checkStateChangedAndSendActions { if(self.state != _priorState) { _priorState = self.state; [self sendActionsForControlEvents:UIControlEventStateChanged]; } } @end

Puede crearlo programáticamente usando un método de init UIButton , o usarlo desde Interface Builder agregando un UIButton normal a su vista y cambiando la clase a MyButton , pero debe escuchar el evento UIControlEventStateChanged programáticamente. Por ejemplo, de viewDidLoad en su clase de controlador como este:

[self.myButton addTarget:self action:@selector(myButtonStateChanged:) forControlEvents:UIControlEventStateChanged];


Subclase UIButton, sobrescribir setState: es lo que funciona para mí. Probablemente esta no sea la mejor manera, pero lo he hecho con éxito.

Disculpas por la respuesta anterior, estaba mal. Debería haber mirado mi código. En mi caso, solo necesitaba cambiar el estado basado en resaltado, así que setHighlight: - setHighlight: para cambiar los valores que necesitaba. YMMV.


[self addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionNew context:nil];

Funciona bien si verifica la propiedad ''seleccionada'' del observador.

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqualToString:@"selected"]) { [self.img setImage:self.selected ? self.activeImg : self.inactiveImg]; } else [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; }