views:

1033

answers:

4

I'm having some problems displaying the contents of one NSArrayController in two windows defined in different XIBs.

In my main window (in MainMenu.xib), I have an NSTableView which is bound to an NSArrayController

In my second XIB, I have another window that contains an NSTableView. I created a new NSArrayController, and bound the table to the contents of that NSArrayController.

Both NSArrayControllers are bound to the exact same NSArray.

Everything is fine at first, but the problem is that if an object is added to the array using the NSArrayController in the main window, the secondary window's view of the data isn't updated. This is most likely because it doesn't know that it needs to update itself, since its NSArrayController wasn't used to add the new object.

What I would like to do is use the exact same instance of NSArrayController in both windows. This way if an object is added to the array, both views will be notified of the change.

The problem is that I can't figure out how to do this in Interface Builder. I can't bind an NSArrayController to another NSArrayController (I get a runtime error indicating that it can only be bound to an NSArray). Connecting the NSArrayController to the NSArrayController member of File's Owner won't help either, since that will just wipe out the NSArrayController that I want it to use.

I guess I could set the bindings up myself in code, but it seems like it would be better to use Interface Builder if possible. Is there a way to do this in Interface Builder, or maybe a better way to set this up all together?

Update: In response to Chuck's answer and comments, I tried the following: -Bound the Table Content of my NSTableView to the arrangedObjects of the NSArrayController member (if I used the NSArrayController itself, an error appeared in the log: "[NSArrayController count]: unrecognized selector sent to instance") -Bound each column in the table to File's Owner, with a Model Key Path of arrayController.arrangedObjects.propertyName

This still did not result in the contents of the table getting updates. I think this is because I am binding to the arrangedObjects of the NSArrayController, rather than to the NSArrayController itself. But, if I bind to the NSArrayController directly, that gives me errors.

Normally, I would select my NSArrayController from the "Bind to" ComboBox, use arrangedObjects as the Controller Key, and the property itself as the Model Key Path. I'm not sure how to do the equivalent of that in this case - if I can at all.

Did I do something wrong in the process above?

A: 

There's no reason having two array controllers should make one view not update when you update through another one. If it's not happening, it sounds like your KVO notifications are getting lost somewhere. Most likely you are directly editing an array (in an NSArrayController subclass's add: method, perhaps?) without sending the proper change notifications.

Chuck
How do I do that, exactly? I can't bind an NSArrayController in the 2nd XIB to the one in File's Owner, since NSArrayControllers can only be bound to NSArrays. What do I use for the path in my NSTableView to pull in the data from that NSArrayController?
Andy
It's just normal key-value coding. Say your file's owner stores a reference to the controller as `arrayController`. You'd bind the table view to File's Owner with the model key path "arrayController".
Chuck
But then how does the table know what to display in each column? I typically bind a column to the arranged objects of an NSArrayController defined in the XIB. How do I get to the bound NSArrayController to display the correct values in each column?
Andy
You get to the NSArrayController with File's Owner.arrayController, like I said. It's the same as binding to an NSArrayController normally, only you're accessing it through File's Owner instead of directly in your nib.
Chuck
I still can't get it to work. :( I described what I did in an edit to my question - did I do something wrong based on what you described above?
Andy
I've edited my answer to get a bit closer to the root of the problem. Sorry for the time-wasting previous answer. I've been under the weather and I guess my brain was a bit muddled. You can safely ignore what I said previously, because it's extra work that doesn't get to the heart of the problem.
Chuck
No worries, ty for all the help so far. :)I'm using NSArrayController's addObject: method (not using a subclass). Is there something that I should be doing in addition to this?
Andy
+1  A: 

The best option that I've been able to come up with is to notify the second NSArrayController that the array it's managing has changed by calling rearrangeObjects: on it. That seems clunky, but it works.

Andy
+1  A: 

What about setting them up as proxy objects in IB then instantiating the actual in code, perhaps in your app delegate?

Ryan Townshend
The only reference that I could find to custom proxy objects is http://developer.apple.com/documentation/DeveloperTools/Conceptual/IB_UserGuide/EditingNibFileObjects/chapter_4_section_4.html#//apple_ref/doc/uid/TP40005344-CH12-SW2, but that is for iPhone only. Did I miss something?
Andy
+1  A: 

You can get both NSArrayControllers to update via KVO when the array is added to. The trick is that you have to observe and add to the array in a KVO compliant way.

You'd want some object that owns the array, let's call it "model". The array has to be a key on the object, let's call the key "contentArray". Next, when you're adding to/removing from, you should do it by first calling mutableArrayForKey on "model" to get "contentArray". Then the addition/removal from the array should work, for example:

Controller1 is bound to self.model.contentArray

Controller2 is bound to self.model.contentArray

//this method is on the "model" object
-(void)addContent(id content)
{
    NSMutableArray* contentArr = [self mutableArrayForKey:@"contentArray"];
    [contentArr addObject:content]; //this will trigger KVO notifications
}

Alternatively you could do the KVO notifications manually like this:

-(void)addContent(id content)
{
    [self willChangeValueForKey:@"contentArray"];
    [m_contentArray addObject:content];
    [self didChangeValueForKey:@"contentArray"];
}
Tom Dalling