chrisgoyal
chrisgoyal

Reputation: 4347

NSMenuItem's value binding with BOOL

I am having some issues binding an NSMenuItem's "value" binding to a BOOL.

I simplified the problem to this:

1) The menu item must call the action method that changes the value of the BOOL otherwise it doesn't work (i.e. if an NSButton calls a method that changes the value of the BOOL then the menu item won't update)

2) Even if the action method makes the BOOL a constant (i.e. enabled = YES), the "value" of the menu item still alternates.

Any ideas? I'm so confused!

Here is the code:

MenuBindings_AppDelegate.h

#import <Cocoa/Cocoa.h>

@interface Menu_BindingsAppDelegate : NSObject <NSApplicationDelegate> 
{   
   BOOL foo;
}

- (IBAction)toggle:(id)sender;
- (IBAction)makeYes:(id)sender;

@property BOOL foo;

@end

Menu_BindingsAppDelegate.m

@implementation Menu_BindingsAppDelegate

@synthesize foo;

- (IBAction)toggle:(id)sender
{
   [self setFoo:!foo];
}

- (IBAction)makeYes:(id)sender
{   
   [self setFoo:YES];
}

@end

In my nib, I have a button connected to the -makeYes: action and a menu item connected to the -toggle: action. The menu item's "value" binding is bound to the app delegate's "foo" attribute.

Thanks.

Upvotes: 3

Views: 2319

Answers (1)

Rob Keniger
Rob Keniger

Reputation: 46020

Cocoa Bindings uses Key-Value Observing (KVO) to obtain notification of changes in model objects. In order for the change in the model (your BOOL value) to be noticed by observers including any views that use bindings, you must update the model using Key-Value Coding-compliant accessor methods. If you just set the value of the ivar directly, no KVO notifications will be sent.

You can either implement the KVC accessors yourself or declare a property and use the @synthesize keyword in your implementation to have the compiler create compliant accessors for you.

This is how you would implement KVC-compliant accessors:

//YourModel.h
@interface YourModel : NSObject
{
    BOOL enabled;
}
- (BOOL)enabled;
- (void)setEnabled:(BOOL)flag;
@end

//YourModel.m
@implementation YourModel
- (BOOL)enabled
{
    return enabled;
}
- (void)setEnabled:(BOOL)flag
{
    enabled = flag;
}
@end

and this is how you would do the same thing using Objective-C 2.0 property syntax:

//YourModel.h
@interface YourModel : NSObject
{
    BOOL enabled;
}
@property BOOL enabled;
@end

//YourModel.m
@implementation YourModel
@synthesize enabled;
@end

You can then call [yourModel setEnabled:YES] and any registered KVO observers (including your menu binding) will be informed of the change.

Alternatively, you can call yourModel.enabled = YES which will use the proper KVC accessors if available.

I uploaded a sample project to demonstrate how it's done.

Upvotes: 2

Related Questions