views:

83

answers:

3

I have an NSTextField in my view. Its value is bound to an NSNumber *number in my controller. The controller simply calls through to the model (value) to get the appropriate value.

// In the controller
- (NSNumber *)number {
    return [NSNumber numberWithFloat:[model value]];
}
- (void)setNumber:(NSNumber *)aNumber {
    [model setValue:[aNumber floatValue]];
}

This is fine, only the controller is not notified of model changes and thus, changing the value in the model does not update the NSTextField.

The only other option I can think of is to have the model notify the controller and the controller manually update the view through an Outlet. But this circumvents the binding.

// In the model, after value change
[[NSNotificationCenter defaultCenter] postNotificationName:@"ValueChanged" object:self];

// In the controller, after being notified
- (void)updateView:(NSNotification *)aNotification {
    [myTextField setFloatValue:[model value]];
}

Is there a better, binding-aware way to implement this communication?

+2  A: 

In your controller, override the keyPathsForValuesAffectingValueForKey: class method. Something like:

+(NSSet*) keyPathsForValuesAffectingValueForKey:(NSString*)key
{
   if([key isEqual:@"number"])
      return [NSSet setWithObject:@"model.value"];
   return [NSSet set];
}

(I'm not at my Mac so this is untested; but the general idea should be sound)

James Williams
+2  A: 

Another option (and what I usually do myself, purely for personal preference) is something like (in the controller):

-(void)setModel:(id)m
{
   [model removeObserver:self forKeyPath:@"value"];

   [m retain];
   [model release];
   model = m;

   [model addObserver:(self) forKeyPath:@"value" options:NSKeyValueObservingOptionNew context:NULL];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
   if([keyPath isEqual:@"value"])
   {
      [self willChangeValueForKey:@"number"];
      [self didChangeValueForKey:@"number"];
   }  
}

Again, this code is untested, but the general idea should work.

James Williams
Are bindings into the model a good idea? I have a strangely bad feeling about this that I can't explain.
BastiBechtold
Well, it's not bindings. It's KVO. Bindings are built on top of KVO, but KVO is separate. And I don't know that it's either a good idea or a bad idea. KVO is just a tool. I like to let my controller observe my view because it feels less coupled to me. The model doesn't even have to know the controller exists. I'm sure alternate viewpoints exist.
James Williams
+1  A: 

I have an NSTextField in my view. Its value is bound to an NSNumber *number in my controller. The controller simply calls through to the model (value) to get the appropriate value.

Why is this property wrapping the value in an NSNumber? KVC will convert primitive values, such as floats, to and from objects for you.

This is fine, only the controller is not notified of model changes and thus, changing the value in the model does not update the NSTextField.

The model changing isn't relevant here, and the controller doesn't need to know about the value of the model's property changing, unless the controller is observing that property—in which case, the controller is getting notified of those changes, because you are using the model's accessors for the property.

The problem is that you have not bound the text field through the model—you're binding it to a fake property on the controller instead.

So, assuming the controller has exposed a property for the model object, simply bind the text field to the controller with the key path model.value.

Peter Hosey