Untied II: Table Data Source Tricks

Last time around, I concentrated on what I could and couldn’t get a Cocoa bindings-based NSTableView to do for me.

Today, I’m going to do everything I mentioned last time, using the data source and delegate methods in listed in NSTableView.h.

As a starting parameter, I said that I wanted a special, unmodifiable, always-on-top Category entry in my table, called “All”. Making it uneditable is as easy as implementing the method tableView:shouldEditTableColumn:row: in my delegate class, and making it return NO if row == 0. I like this way better than making an “editable” Category accessor, because it puts the responsibility where it belongs, on the specific table that has the special entry, rather than putting a hack in the Category class to handle it.

Then, the issues. First up was sorting. I sort the contents of my Categories table the way I want, without having the pesky user being able to screw it up, by manually sorting the table’s contents in the data source method that’s called every time the user adds or changes a table entry, tableView:setObjectValue:forTableColumn:row:.

Because I never set the Sort Key in Interface Builder, as far as the table itself is concerned, the contents are completely unsorted. There’s no ascending or descending triangle in the column header.

Scott Anguish, in the comments of “Untied I”, suggested overriding NSArrayController to get the same behavior, and with another tweak (see that post’s comments) that does indeed get me exactly the same behavior. I implemented his suggestion in the Bindings part of the project that comes with today’s post. I complained in my reply that clicking on the column header still ends the editing session. Just a minor annoyance, to be sure, but the Mac is all about getting things exactly right. But it turns out my data source implementation has the same problem, so it’s a draw.

Second was editing, in three parts: starting an editing session immediately on add, resorting the entry immediately after the editing session ends, and beeping and not ending the editing session if the name is invalid.

To understand how I start the editing session, you need to know how I implement the action newCategory:. Before, this invoked the NSArrayController’s add method, which was asynchronous. Now, it does all the data model manipulation itself, and so it can go ahead and start the editing session without any worries afterward.

Resorting the array takes place, as stated above, in the data source method tableView:setObjectValue:forTableColumn:row:. The same code that does it when you first add a category also works when you rename a category.

For the last part, editing verification is available by implementing the NSControl delegate method control:textShouldEndEditing:. This method is not called if I’m using bindings.

The sample is called Categorizer02.zip.