views:

183

answers:

3

Hi,

I'm fairly new to the world of bindings in cocoa, and I'm having some troubles (perhaps/probably due to a misunderstanding).

I have a singleton that contains an NSMutableArray called plugins, containing objects of class Plugin. It has a method called loadPlugins which adds objects to the plugins array. This may be called at any point. It's been added as an instance in Interface Builder.

Also in IB is an NSObjectController, whose content outlet is connected to the singleton. There is also an NSArrayController, whose contentArray is bound to the NSObjectController (controller key is 'selection', model key path is 'plugins', object class name is 'Plugin').

And finally I have a table view with 2 columns, the values of which are bound to the NSArrayController's arrangedObjects, using keys of properties in the Plugin class.

So far so standard (as far as I can tell from tutorials at least). My trouble is that when the loadPlugins method is called in the singleton, and objects are added to the plugins array, the table doesn't update to show the objects (unless loadPlugins is called before the nib is loaded). -reloadData called on the tableView doesn't do anything.

Is there a way to tell the NSArrayController that the referenced array has been updated? I understand there is the -add: method for NSArrayController, which could be used in the loadPlugins, but this isn't desirable as I want to keep the singleton totally separate from the display aspect.

This seems related to: http://stackoverflow.com/questions/1623396/refresh-cocoa-binding-nsarraycontroller-combobox

The line: "editing the array behind the controller's back" seems to perhaps pinpoint the problem, but I would hope that it would be possible to have the singleton not know about the controller.

Thanks in advance.

\edit...

As per TechZen's suggestion, here's the bindings:

TableColumn --('arrangedObjects', 'pluginName', Value)--> NSArrayController
NSArrayController --('selection', 'plugins', ContentArray)--> NSObjectController
NSObjectController --(content)--> PluginsManager
A: 

Is there a way to tell the NSArrayController that the referenced array has been updated?

You shouldn't have to. That is what key-value observing is for. It is the basis of the entire binding functionality. The bound object (in this case the interface element) automatically observes (receives a notification) that the object it observes has changed. You shouldn't ever to have to manually tell it that a change has taken place. If the table does not update then the binding is wrong.

It's hard to tell from a textual description but I think your problem is that you have the NSObjectController set to "selection". That will only work if you've bound another UI element to the NSObjectController and that element selects a data item in the controller.

You might try writing out the bindings something like:

Object1--(controller key, key path, class)--> Object2 
Object2--(controller key, key path, class)--> Object3

You might see the problem and if you post it the rest of us can understand it.

(There needs to be a standard for representing bindings and Core Data relationships textually but I guess we'll muddle through.)

TechZen
A: 

As a somewhat hacky way to get it working (by triggering the appropriate KVO notification), try putting:

[self willChangeValueForKey:@"plugins"];
[plugins addObject:somePlugin];
[self didChangeValueForKey:@"plugins"];

Instead of

[plugins addObject:somePlugin];

Ideally you should implement full on KVC support, then you can use KVC to trigger KVO which will trigger the Bindings update. If you did that, you could then use:

[self insertObject:somePlugin inPluginsAtIndex:0];

For info on what is required for KVC compliance, read this: http://developer.apple.com/mac/library/documentation/cocoa/conceptual/KeyValueCoding/Concepts/Compliant.html.

Nick Forge
+1  A: 

I've figured this out - it was a classic case of RTFM (and Understand-TFM). All came down to KVC compliance: http://developer.apple.com/mac/library/documentation/cocoa/conceptual/KeyValueCoding/Concepts/Compliant.html#//apple_ref/doc/uid/20002172-BAJEAIEE

In the PluginManager Singleton, which had the array called 'plugins', I simply needed to implement:

-insertObject:inPluginsAtIndex:
-removeObjectFromPluginsAtIndex:

And then use those methods when I wanted to add/remove a plugin.

Thanks for the help guys.

Loz
This was monumentally helpful. Thanks for answering your own question--this was a big step for me.
Brian Warshaw