views:

233

answers:

1

Hi,

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.

A: 

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.

Rob Keniger
Thanks for the suggestion. I tried using both willChangeValueForKey:/didChangeValueForKey: and using @property/@synthesize behavior, but I'm still getting odd results...
chrisgoyal
Can you post your code? You don't need to use will/didChangeValueForKey: if you are implementing KVC-compliant accessors.
Rob Keniger
Thanks. Updated with code.
chrisgoyal
Never mind, I figured out the problem. When you click on the menu item, its value automatically gets toggled. Didn't realize that...thanks!
chrisgoyal
Yes, that's because the binding is two-way. I can understand why that could be confusing.
Rob Keniger