NSView, NSScrollView, NSClipView: IMReallyImpressed

I’ve just started working on the graphics part of my program and thus far I’ve been massively impressed by Cocoa’s offerings in this area. Admittedly, I haven’t done a whole lot — I’m currently just drawing using NSString’s drawInRect:withAttributes: (from AppKit’s NSString additions) and some NSBezierPath methods.

The largest source of awe so far has been just how easy it is to place an NSView subclass inside an NSScrollView. This is a task that involves no code whatsoever — one simply has to select the “Make subviews of > Scrollview” command in Interface Builder and your custom view is now scrollable.

I think the most impressive facet of this set up is the elegance of the view hierarchy. Your custom NSView subclass effectively forms a canvas, only a portion of which is displayed in the scroll view. The visible part of the view is generally referred to as the content view (and is an instance of NSClipView) and that’s pretty much it — the scroll view provides the scroll bars and resizes them as the size of the “canvas” changes (I’m using NSView’s setFrameSize: and setFrameOrigin: methods to obtain the desired stretching of my view in response to an NSSlider.) Here’s how it’s looking at the moment:

GFF Viewer window

NSTableView delegate and data source

I’ve just successfully implemented my first “delegate” (and indeed datasource) object in Cocoa, so this post is mostly a result of my excitement for actually making something work. However, making this work is actually not very impressive at all, but I’m still happy, especially when I consider that I didn’t know what a delegate was until last week :)

There’s a great article here that describes what a delegate is, that is:

A delegate is an object which is able to influence the behavior of another object by responding to delegate messages. A class that uses a delegate has a “delegate” instance variable and defines a number of “delegate methods”. An instance of that class will then send delegate messages to its delegate for help on deciding how to behave.

In other words, delegation allows for one object to ask another object to do something on its behalf (much like a delegate in real life). Delegation can, therefore, be a simple alternative to subclassing — rather than implementing a whole new class to customise an object’s behaviour, simply have a delegate object sign up to be invoked when the custom behaviour is required.

The delegate object in this case was my view controller, which needs to respond to the user selecting a different line in my NSTableView. The way to do this involves only 2 steps:

  1. implementing the method – (void) tableViewSelectionDidChange:­(NSNotification *)­notification
  2. connecting up the controller object as the delegate in Interface Builder

In my case, the first stage did literally involve just adding the following code (for now) to my view controller class:
- (void) tableViewSelectionDidChange:(NSNotification *)notification {
int currentRow = [motifList selectedRow];
if (currentRow == -1) {
return;
}
[motifName setTitle:[self tableView:motifList objectValueForTableColumn:motifNameColumn row:currentRow]];
}

The only vaguely interesting bit here is that I send the message tableView:­objectValueForTableColumn:­row: to self. This is because my view controller is also the datasource for the very same NSTableView. motifName, in case you’re wondering is an NSBox.

The second stage is even simpler and involves control-dragging from the NSTableView to the controller object in Interface Builder and selecting the delegate outlet, as shown here:
NSTableView delegation

Implementing the same object as the NSTableView datasource is even simpler, involving only the addition of the following two methods from the (appropriately-named) NSTableDataSource class:

  • – (int)numberOfRowsInTableView:(NSTableView *)theTableView
  • – (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)theRow

In my case (where I’m pulling values from an array) it’s just a case of returning [array count] and [array objectAtIndex:theRow] respectively for those methods. That’s all for now!

iTunes 7.3 changed sort order

I just noticed that iTunes has changed the sort order as of version 7.3. After the update, the band “…And You Will Know Us By the Trail of Dead” no longer appears at the top of my library and instead appears where they would be if they dropped the ellipsis from their name. Similarly, 2 Many DJs, 3 Doors Down and their ilk now appear at the bottom of the list.

The changes in sorting are documented in this Knowledge Base article. From the way the article is worded, it would seem as if the sorting order is fixed in iTunes and doesn’t honour the “Order for sorted lists” (see image below) preference in the International preferences pane, but I haven’t had a chance to verify this.

System-wide sort order

Overall, I think the new sorting makes a lot of sense — numbers are sorted numerically, leading space, punctuation and articles are all ignored and numbers are placed after the alphabet. Good stuff. In many ways, iTunes 7.3 was a solid upgrade even if most of the changes were quite subtle.