Как слушать изменение состояния UIButton?

Я не продлю UIButton С общей функциональностью для изменения определенных атрибутов внешнего вида на основе отображаемого заголовка.

для этого мне нужно обнаружить и отреагировать на изменения в свойстве "состояние". Поэтому я удостоверяюсь, что внешний вид правильно настроен, если пользователь установил разные названия для разных состояний. Я предположил, что мне нужно будет использовать какой-то KVO, например:

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

но это, похоже, не увольняет observeValueForKeyPath:... метод для @ "state"или @"currentTitle". Я предполагаю, что это связано с тем, что UIButton не реализует шаблон KVO для этих свойств.

Я не хочу просто слушать щелчки. Эти события вызывают изменение состояния, но не единственные возможные причины.

кто-нибудь знает способ слушать и реагировать на изменения состояния UIButton?

спасибо


обновление

просто к сведению так как я узнал несколько вещей за последние пару лет ;).

С тех пор я разговаривал с некоторыми людьми Apple, которые знают, и причина, по которой KVO не работает на государственной собственности, связана с тем, что нет UIKit гарантированно будет KVO уступчивым. Подумал, что стоит повторить здесь-Если вы пытаетесь слушать любой свойство класса UIKit framework, имейте в виду, что он может работать, но официально не поддерживается и может сломаться на разных iOS версии.

4 ответов


хорошо, я нашел решение, которое работает. Вы можете прослушать свойство text titleLabel кнопки.

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

Кажется, что он увольняется дважды за изменение, поэтому вы должны проверить, чтобы убедиться, что значения @"old" и @"new" в переданном словаре изменений отличаются.

примечание: не используйте @"старый" и @"новый" напрямую. Константами являются nskeyvaluechangeoldkey и nskeyvaluechangenewkey соответственно.


мне это нужно было сегодня, поэтому я написал этот класс, который выполняет эту работу:

моя кнопка.h

#import <UIKit/UIKit.h>

// UIControlEventStateChanged uses the first bit from the UIControlEventApplicationReserved group
#define UIControlEventStateChanged  (1 << 24)

@interface MyButton : UIButton
@end

моя кнопка.м

#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

вы можете создать его программно с помощью UIButton init метод, или использовать его из Interface Builder, добавив нормальный UIButton на ваш взгляд и изменение класса на MyButton, но вы должны слушать для UIControlEventStateChanged событие программно. Например,viewDidLoad в вашем классе контроллера, как это:

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

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

отлично работает, если вы проверяете внутри наблюдателя "выбранное" свойство

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

подкласс UIButton, override setState: это то, что работает для меня. Это, вероятно, не лучший способ, но я сделал это успешно.

извинения за приведенный выше ответ, это было неправильно. Надо было взглянуть на мой код. В моем случае мне нужно было только изменить состояние на основе выделения, поэтому я перешел -setHighlight: чтобы изменить любые значения, которые мне нужны. YMMV.