禁用按钮循环(NStimer 或 MouseMove)XCode Objective-C MacOS

Disable Button Loop (NStimer or MouseMove) XCode Objective-C MacOS

我只是尝试在 XCode MacOS(不是 iOS)上使用各种方法禁用按钮 Cocoa Objective-C.

在这种情况下,我有一个帮助按钮 (m_btHelp),当 g_bEnableHelpButton = NO 时该按钮被禁用;但只有在鼠标移动时才会检查

-(void)mouseMoved:(NSEvent *)theEvent
{
    
    if(g_bEnableHelpButton) {
        [m_btHelp setEnabled:YES];
    } else {
        [m_btHelp setEnabled:NO];
    }

我宁愿连续检查而不是只在鼠标移动时检查。我已经用这样的东西尝试过 NSTimer,但它似乎不起作用(m_btHelp 在 g_bEnableHelpButton = NO 时不会被禁用;就像在 mouseMoved 事件中一样:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(Timerloop) userInfo:nil repeats:YES];
}

- (void)Timerloop
{
     if(g_bEnableHelpButton) {
         [m_btHelp setEnabled:YES];
     } else {
         [m_btHelp setEnabled:NO];
     }
}

g_bEnableHelpButton是全局变量吧?不要使用全局变量。最好创建一个 class 来保存您的状态(可以是视图模型,...)。我将在下面的所有示例中跳过状态 class 并将在同一视图控制器上使用 BOOL helpButtonEnabled 属性 (这不是强制性的,它只是使所有这些示例更短).你可以把这个属性移到别处,它可以是一个状态class,它基本上可以是任何对象。

另一件事是 NSTimerNSTrackingArea、...所有这些都在浪费 CPU 周期、电池寿命... Cocoa & Objective-C 提供了多种方式来监控 属性 值并对其做出反应。你可以覆盖 属性 setter,你可以使用 KVO 或绑定。以下示例涵盖了所有三种方法。

肯定还有其他方法(比如 ReactiveCocoa),但我想演示三种方法如何在没有依赖项的情况下实现它。

初始状态

假设你有这样的观点:

实施如下:

#import "ViewController.h"

@interface ViewController ()

// Help button from the Main.storyboard
// Imagine it's your m_btHelp
@property (nonatomic, strong) IBOutlet NSButton *helpButton;

// Property driving helpButton enabled/disabled state
// Imagine it's your g_bEnableHelpButton
@property (nonatomic, getter=isHelpButtonEnabled) BOOL helpButtonEnabled;

@end

@implementation ViewController

// Just another button action coming from the Main.storyboard which toggles
// our helpButtonEnabled property value
- (IBAction)toggleHelpButtonEnabled:(id)sender {
    self.helpButtonEnabled = !self.helpButtonEnabled;
}

@end

有帮助按钮和切换按钮,它只切换 helpButtonEnabled 值(YES -> NONO -> YES)。

如何在没有计时器、跟踪区域、...以更新帮助按钮状态的情况下进行监视?

覆盖setter

Encapsulating Data.

@implementation ViewController

// This is setter for the helpButtonEnabled property.
- (void)setHelpButtonEnabled:(BOOL)helpButtonEnabled {
    // If the new value equals, do nothing
    if (helpButtonEnabled == _helpButtonEnabled) {
        return;
    }

    // Update instance variable
    _helpButtonEnabled = helpButtonEnabled;
    // Update button state
    _helpButton.enabled = helpButtonEnabled;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // When the view loads update button state to the initial value
    _helpButton.enabled = _helpButtonEnabled;
}

// Just another button action coming from the Main.storyboard which toggles
// our helpButtonEnabled property value
- (IBAction)toggleHelpButtonEnabled:(id)sender {
    self.helpButtonEnabled = !self.helpButtonEnabled;
}

@end

KVO

Introduction to Key-Value Observing Programming Guide.

static void * const ViewControllerHelpButtonEnabledContext = (void*)&ViewControllerHelpButtonEnabledContext;

@implementation ViewController

- (void)dealloc {
    // Remove previously registered observer when the view controller goes away
    [self removeObserver:self forKeyPath:@"helpButtonEnabled" context:ViewControllerHelpButtonEnabledContext];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // Register observer for the helpButtonEnabled key path
    //  - it fires immeditately with the current value (NSKeyValueObservingOptionInitial)
    //  - it fires later every single time new value is assigned (NSKeyValueObservingOptionNew)
    //  - context is used to quickly distinguish why the observeValueForKeyPath:... was called
    [self addObserver:self
           forKeyPath:@"helpButtonEnabled"
              options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
              context:ViewControllerHelpButtonEnabledContext];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if (context == ViewControllerHelpButtonEnabledContext) {
        // It's our observer, let's update button state
        _helpButton.enabled = _helpButtonEnabled;
    } else {
        // It's not our observer, just forward it to super implementation
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

// Just another button action coming from the Main.storyboard which toggles
// our helpButtonEnabled property value
- (IBAction)toggleHelpButtonEnabled:(id)sender {
    self.helpButtonEnabled = !self.helpButtonEnabled;
}

@end

绑定

Introduction to Cocoa Bindings Programming Topics.

@implementation ViewController

- (void)dealloc {
    // Remove binding when the view controller goes away
    [self.helpButton unbind:NSEnabledBinding];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // self.helpButton.enabled is binded to self.helpButtonEnabled
    [self.helpButton bind:NSEnabledBinding
                 toObject:self
              withKeyPath:@"helpButtonEnabled"
                  options:nil];
}

// Just another button action coming from the Main.storyboard which toggles
// our helpButtonEnabled property value
- (IBAction)toggleHelpButtonEnabled:(id)sender {
    self.helpButtonEnabled = !self.helpButtonEnabled;
}

@end