Search code examples
objective-ccocoacocoa-bindingskey-value-observingnsarraycontroller

How to get notified of changes to models via an NSArrayController?


I have an NSView subclass which is bound to the arrangedObjects of an NSArrayController. When the array has an item inserted or removed the view is notified. How do I get it to be notified if a model stored in the array has an attribute changed?

Do I need to add my view as an observer to every (relevant) attribute of every item added to the array?

When an item is added to or removed from the array I am notified via observeValueForKeyPath:ofObject:change:context: in my NSView subclass. I am not notified of changes to the models stored in the array but I could, every time I am notified of an insertion, add the view as an observer to the new item's attributes. Is this the best way to do this?

I overrode addObserver for the model class so that I could see what happens and noticed that NSTableView columns bound to the arrangedObjects add themselves as observers to the appropriate attributes. Can this be made to happen automagically or do I set up the observations manually?


Solution

  • A big thank you to dreamlax but I think I didn't do a good enough job explaining my problem. My model class was observable and produced the right notifications but I couldn't work out how to observe them without observing every item in the array directly.

    I think the documentation for key paths could be improved because I couldn't find anything that explained the very simple change I needed to make. There's some good info the array magic keypaths but no simple "these are the common things" documentation.

    Anyway. Previously in my NSView subclass I had the following:

    - (void) bind:(NSString *)binding toObject:(id)observable withKeyPath:(NSString *)keyPath options:(NSDictionary *)options
    {
      if ([binding isEqualToString:@"observedObjects"]) {
        [observable addObserver:self forKeyPath:@"arrangedObjects" options:0 context:nil];
      } else {
        [super bind:binding toObject:observable withKeyPath:keyPath options:options];
      }
    }
    

    To get notification of changes to the models within the NSArrayController's arrangedObjects all I needed to add was observation of arrangedObjects.name (for the name property of my model). So the above code became:

    - (void) bind:(NSString *)binding toObject:(id)observable withKeyPath:(NSString *)keyPath options:(NSDictionary *)options
    {
      if ([binding isEqualToString:@"observedObjects"]) {
        [observable addObserver:self forKeyPath:@"arrangedObjects" options:0 context:nil];
        [observable addObserver:self forKeyPath:@"arrangedObjects.name" options:0 context:nil];
      } else {
        [super bind:binding toObject:observable withKeyPath:keyPath options:options];
      }
    }
    

    That's it! Now if any object in arrangedObjects gets its name changed I am notified.