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