Before writing my previous post, I had asked some people on IRC for help, including David Smith, who is a fellow Adium developer. He remembered a solution, but uncertainly. One thing he said, though, proved relevant: “You have two NSTableColumns; which one is it [NSTableView] creating the content binding from?”
David was referring to the content
binding of NSTableView, which I had dismissed as an artifact of an earlier design of NSTableView, or perhaps a simplified path for those people who had only one column in their table view. (I saw the mention in the docs that the table view binds it automatically to the value of one of its columns’ value
bindings, but didn’t make any connections then.)
This latter explanation made the content
binding incompatible with my app, since I had two parallel arrays of NSString
s (one array contained keys, and the other contained values). So I continued looking for a solution, and eventually gave up and wrote the post.
After I published it, Scott Anguish posted a comment on it:*
you can’t have simple arrays of strings as the content of an array controller. it doesn’t work. You need to have an array of objects of some sort that have an attribute, even if they’re just NSDictionaries with a single attribute called “string”.
I don’t believe that NSArrayController
has a special case for NSString
, but Scott’s comment got me thinking. I added a category to NSString
that added a -selfString
method, then added that name as the model key path in the table columns’ value
bindings. I was wondering, you see, whether this would be enough to satisfy NSTableColumn
and NSArrayController
.
It still didn’t work. At this point, I had the idea to fire up F-Script Anywhere and have a look around. I did that, and was digging down to the table columns when I noticed this:
That’s FSA’s object browser, showing the table view. So the table view’s content
binding was bound to the keys array controller, with a model key path of lastMessageKeys
.
That’s why the keys were showing up fine—they were in the array that was in the table view’s content
binding. The values array was a different array with a different array controller, so NSTableView
threw up its hands and simply showed the entire array’s description.
This is the point that Scott came close to: NSTableView
expects you to have one array of model objects—no more. My parallel arrays of NSString
s do not qualify, because only one of the array controllers can be the value of the table view’s content
binding.
So what role do the columns play? Well, each row in the table view (and by that, I mean *the whole row*) corresponds to one of those model objects. The value in each column is then determined by the model key path in the value
binding of the corresponding NSTableColumn
.
Thus, all the NSTableColumn
s’ value
bindings must be bound to the same array controller and the same controller key, and each column should be bound to a different model key path. Your columns’ value
bindings should look like this:
Column “Foo” |
Column “Bar” |
Bind to: Your one array controller |
Controller key: arrangedObjects (or whatever) |
Model key path: foo |
Model key path: bar |
In my case, of course, this required creating a new model class for a key-value pair, to replace the parallel arrays of NSString
s. This is a good thing, since it brings conceptual purity to my design. Remember, Cocoa is designed around MVC; I didn’t have enough M in my app, which was the problem.
So, in summary, here’s the fix for an NSTableColumn
that shows the entire array in every row:
- Replace your evil parallel arrays with a model class, and a single array of instances of that class.
- Delete all but one of your array controllers, since you now have only one array. Update the remaining controller’s model key path to point to your new array (I assume you renamed it).
- Bind all of the table columns to your new One True Array Controller, distinguishing the columns only by the model key path, which identifies which property you want the column to show for each model object.
Many thanks to both David and Scott for their help in discovering the root of the problem.
* Scott posted his comment on the previous post with no last name; he identified himself in a comment on this post. ↶