24

我正在扩展UIButton通用功能以根据显示的标题更改某些外观属性。

为此,我需要检测并响应“状态”属性的变化。如果用户为不同的状态设置了不同的标题,我会确保正确调整外观。我假设我需要使用某种 KVO,如下所示:

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

但这似乎不会触发 @"state" 或 @"currentTitle" 的 observeValueForKeyPath:... 方法。我认为这是因为 UIButton 没有为这些属性实现 KVO 模式。

我不想只听点击。这些事件会导致状态变化,但不是唯一的潜在原因。

有谁知道一种方法来监听和响应 UIButton 的状态变化?

谢谢


更新

只是一个说明,因为我在过去几年中学到了一些东西;)。

从那以后,我与一些知道的 Apple 人员进行了交谈,KVO 不适用于 state 属性的原因是UIKit 的NONE保证符合 KVO 的事实。认为这值得在这里重复——如果您尝试监听UIKit 框架类的任何属性,请注意它可能有效但不受官方支持,并且可能会在不同的 iOS 版本上中断。

4

4 回答 4

11

好吧,我想出了一个可行的解决方案。您可以收听按钮的 titleLabel 的 text 属性。

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

每次更改似乎都会触发两次,因此您应该检查以确保传递的更改字典中的 @"old" 和 @"new" 的值不同。

注意:不要直接使用@"old" 和@"new"。常量分别是 NSKeyValueChangeOldKey 和 NSKeyValueChangeNewKey。

于 2010-03-23T21:58:28.787 回答
9

我今天需要这个,所以我写了这个类来完成这项工作:

我的按钮.h

#import <UIKit/UIKit.h>

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

@interface MyButton : UIButton
@end

我的按钮.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 使用它,但您必须以编程方式侦听事件。例如,在您的控制器类中,如下所示:UIButtonMyButtonUIControlEventStateChangedviewDidLoad

[self.myButton addTarget:self 
                  action:@selector(myButtonStateChanged:) 
        forControlEvents:UIControlEventStateChanged];
于 2012-06-22T22:08:58.713 回答
2
[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];
}
于 2013-12-05T17:53:54.673 回答
-2

子类 UIButton,覆盖 setState:对我有用。这可能不是最好的方法,但我已经成功地做到了。

为上面的答案道歉,这是错误的。实际上应该查看我的代码。就我而言,我只需要根据突出显示更改状态,因此我覆盖了 -setHighlight:更改我需要的任何值。YMMV。

于 2010-03-23T21:22:43.760 回答