Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sharing an NSArrayController between multiple views in separate NIB files

Tags:

cocoa

First, some background: I am trying to implement a master-detail interface in Cocoa (for OS X). That is, I have a window with two NSTableViews that display two different types of objects. For this question, let's say they are warehouses and packages (to pick an example that is analogous to my actual problem.) Selecting a row in the first table view (on a warehouse) will display a list of packages which belong to that warehouse in the second table view. For the model part, I currently have an NSMutableArray called warehouses of warehouse objects, and each warehouse object has an NSArray of package objects. One thing to note is that the warehouses variable is modified after the NIB files are loaded, so the NSArrayController has to be notified.

Now, I've tried to organize it so that the "master" is in its own view object, and the "detail" is in its own view object. This means that there are three NIBs: a WarehousesView NIB, WarehouseDetailView NIB, and a MainWindow NIB.

  • The WarehousesView NIB contains an instance of a WarehousesViewController (subclassed from NSViewController) and the view itself.
  • The WarehouseDetailView NIB contains an instance of WarehouseDetailViewController and the view itself.
  • The MainWindow NIB contains the main window, an instance of MainWindowController, and an instance of both WarehousesView and WarehouseDetailView. The window itself contains an NSSplitView, and the views of the split view are connected to the corresponding view instances in the NIB file.

That brings me to the first half of my question:

1) Is this a good way of splitting up the application views for a Cocoa application? To me it makes sense, because at a later point more details about the warehouse besides a list of its package inventory might be added to the WarehouseDetailView.

It's an important question, because everything works just fine if I skip creating views, putting all controls in the window directly and put everything else, including NSArrayController instances corresponding to Warehouses and Packages, into the same NIB file. I don't need to ask the second half of the question if I shouldn't even be doing it this way.

The second half of the question is basically:

2) Where should I place the NSArrayControllers corresponding to Warehouses and Packages if I split it up as described above so that the master-detail interface still works? Currently I am using Cocoa bindings, so somehow the content array of the Warehouse NSArrayController needs to bind to my warehouses array, and the content array of the Packages NSArrayController needs to bind to the selection of the Warehouse NSArrayController

I've tried a few things, and I couldn't get anything to work completely. Specifically, I've tried putting the NSArrayController for Warehouses into the WarehousesView NIB and the NSArrayController for Packages into the WarehouseDetailView NIB. The problem with this approach is that I cannot figure out a way to bind the Package NSArrayController to the selection of the Warehouse NSArrayController. The other thing I've tried is (1) putting both NSArrayControllers into the MainWindow NIB, (2) connecting those NSArrayControllers to IBOutlets in the MainWindowController, then (3) passing those variables to their respective view controllers via their constructors, (4) exposing them as properties in the view controllers via KVC, and (5) binding the necessary table columns in a view to the array controller through the File's Owner. The result was that nothing appeared, but there were no errors either. If one of these approaches is the preferred way to do it, I can give more details to help see if I am doing it incorrectly.

Thanks in advance!

Edit: I did look at this related question, and they seemed to be using separate instances of NSArrayControllers for each NIB file if I understood it correctly, and that didn't seem to make sense from a design point of view, but perhaps I am wrong?

like image 752
Stephen Booher Avatar asked Mar 29 '11 18:03

Stephen Booher


1 Answers

Part 1: You can certainly do this. I'd say it's a matter of preference. Personally, then, if the views were going to be displayed simultaneously in a window, I would keep them in the same nib.* Modularity is also a good thing, though.

Part 2: You can put the array controllers wherever you like, really. The only thing you need to worry about is getting each object the references that it needs to the information you want it to have. If you want my 2¢, I'd say put each in the nib with the view its contents will be displayed in. That'll make your detail view setup more difficult, but it continues the modularity you seem to be going for.

You have to remember that every object in the nib is a real instance. The nib allocates and inits them for you; if you put a MyClass object in one nib, and a MyClass object in another nib, those are two different objects. This is sometimes a tricky thing about nibs: it's really convenient to have instances automatically created for you, but it also means some fiddling around with references when you want to do things across nibs.

It sounds like you put instances of WarehouseView and WarehouseDetailView into both your individual nibs and MainMenu.nib and expected them to be the same objects. It won't be so. You have to link objects in the nibs to objects that they already know about. You'll have to work this out for your particular situation.

I don't know where your model is stored, or how you're getting the nib loaded. Whichever object it doing that loading, though, will likely be your link between the individual nib and the rest of the app. This is what the File's Owner proxy object in nibs is for -- it gives you a place to hook up objects in the nib to code that they wouldn't otherwise know about.


*: If you find it easier to layout the views if they are not enclosed in the split view in IB, you could set them up on their own: put the custom view objects in the MainMenu.xib window and you can open each view in its own IB window (though it will not be in a window in the app). Then set the split view's subviews in something's awakeFromNib.

like image 185
jscs Avatar answered Oct 30 '22 23:10

jscs