Подтвердить что ты не робот

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

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

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

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

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

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

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

Спасибо


UPDATE

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

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

4b9b3361

Ответ 1

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

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

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

ПРИМЕЧАНИЕ. Не используйте @ "old" и @ "new" напрямую. Константами являются NSKeyValueChangeOldKey и NSKeyValueChangeNewKey соответственно.

Ответ 2

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

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

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

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

Ответ 3

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

Прекрасно работает, если вы проверяете свойство "selected" наблюдателя

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

Ответ 4

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

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