NSCollectionView Tips
04/11/08 11:55
Recently I’ve been teaching myself Cocoa. I’ve been following the excellent Cocoa Programming For Mac OS X by Aaron Hillegass. Quality reference material like this book and Apple’s documentation makes learning much easier. Apple’s material is consitent, consisce and in the most part complete. However, there is one class where Apple’s reference is quite poor; NSCollectionView.
NSCollectionView is similar to NSTableView; they both display data with the help of a prototype which is copied for each piece of data to be displayed. NSTableView uses the NSCell for the prototype. The NSCell draws its self directly onto the NSTableView. NSCollectionView uses NSCollectionViewItem for the prototype. NSCollectionViewItem is a simple controller (it inherits directly from NSObject), it has no visual element. NSCollectionViewItem has two properties, representedObject and view. representedObject holds the object that the view will display and is set by the NSCollectionView. It is the responsibility of the NSCollectionViewItem to provide the view object.
When an NSCollectionView is created in Interface Builder two additional objects are created:
Well, it’s great for a short while. Problems arises when you want more than simple bindings between the view and the representedObject. There are two parts to this problem:
The next problem is that unlike the bindings the in the NSView, the IBOutlet’s specifed in our NSCollectionViewItem subclass are not connected when the prototype is copied. So how do we connect the IBOutlet’s specified in our NSCollectionViewItem subclass to the controls in the view? This problem is trivial once you realise that Interface Builder is not being very clever.
Interface Builder puts the custom NSView in the same nib as the NSCollectionView and NSCollectionViewItem. This is dumb. The solution is to move the NSView to its own nib and get the controller to load the view programmatically:
[NSBundle LoadFromNib:@"viewItem" owner: result];
//we can configure other aspects of result too [result setPopupMenuDelegate: [self popupMenuDelegate];
return result; } /*This might not be the best place for LoadFromNib:. If it was place in setRepresentObject: we could load different views depending on the class of the representedObject.*/
Problem solved. We now have much more control of NSCollectionView. (Remember you can still bind to representObject).
NSCollectionView is similar to NSTableView; they both display data with the help of a prototype which is copied for each piece of data to be displayed. NSTableView uses the NSCell for the prototype. The NSCell draws its self directly onto the NSTableView. NSCollectionView uses NSCollectionViewItem for the prototype. NSCollectionViewItem is a simple controller (it inherits directly from NSObject), it has no visual element. NSCollectionViewItem has two properties, representedObject and view. representedObject holds the object that the view will display and is set by the NSCollectionView. It is the responsibility of the NSCollectionViewItem to provide the view object.
When an NSCollectionView is created in Interface Builder two additional objects are created:
- An NSCollectionViewItem which is connected to the prototype outlet of the NSCollectionView
- An NSView which is connected to the view outlet of the NSCollectionViewItem
Well, it’s great for a short while. Problems arises when you want more than simple bindings between the view and the representedObject. There are two parts to this problem:
- where do we put our controller code?
- how do we access the IBOutlets specified in our controller code?
The next problem is that unlike the bindings the in the NSView, the IBOutlet’s specifed in our NSCollectionViewItem subclass are not connected when the prototype is copied. So how do we connect the IBOutlet’s specified in our NSCollectionViewItem subclass to the controls in the view? This problem is trivial once you realise that Interface Builder is not being very clever.
Interface Builder puts the custom NSView in the same nib as the NSCollectionView and NSCollectionViewItem. This is dumb. The solution is to move the NSView to its own nib and get the controller to load the view programmatically:
- Move the NSView into its own nib (thus breaking the connection between the NSCollectionViewItem and NSView).
- In I.B., change the Class Identity of File Owner to the NSCollectionViewItem subclass.
- Connect the controls to the File Owner outlets.
- Finally get the NSCollectionViewItem subclass to load the nib:
[NSBundle LoadFromNib:@"viewItem" owner: result];
//we can configure other aspects of result too [result setPopupMenuDelegate: [self popupMenuDelegate];
return result; } /*This might not be the best place for LoadFromNib:. If it was place in setRepresentObject: we could load different views depending on the class of the representedObject.*/
Problem solved. We now have much more control of NSCollectionView. (Remember you can still bind to representObject).