views:

46

answers:

1

I have an NSTableView that binds via an NSArrayController to an NSMutableArray. What's in the array are derived classes; the first few columns of the table are bound to properties that exist on the base class. That all works fine.

Where I'm running into problem is a column that should only be populated if the row maps to one specific subclass. The property that column is meant to display only exists in that subclass, since it makes no sense in terms of the base class. The user will know, from the first two columns, why the third column's cell is populated/editable or not.

The binding on the third column's value is on arrangedObjects, with a model path of something like "foo.name" where foo is the property on the subclass. However, this doesn't work, as the other subclasses in the hierarchy are not key-value compliant for foo.

It seems like my only choice is to have foo be a property on the base class so everybody responds to it, but this clutters up the interfaces of the model objects.

Has anyone come up with a clean design for this situation? It can't be uncommon (I'm a relative newcomer to Cocoa and I'm just learning the ins and outs of bindings.)

A: 

One way to solve this is to only use bindings for the first two column where it works. For the remaining columns you implement a NSTableViewDataSource that implements the custom logic needed.

First create a new class that implements

-tableView:objectValueForTableColumn:row:
-tableView:setObjectValue:forTableColumn:row:

(you only need the 2nd if the user is supposed to edit the other columns). Then add an

IBOutlet NSArrayController *valuesController;

instance variable to that class.

Inside Interface Builder add a new object of that class (drag a blue "object" cube into the file's window from the library). Connect your array controller to your new data source. Then connect the data source with the table view to make it the table view's data source.

Make sure the columns past the first two are not bound to anything.

The

-tableView:objectValueForTableColumn:row:

method will get called for those columns as needed and you can look at the object as needed:

- (id)tableView:(NSTableView *)aTableView
   objectValueForTableColumn:(NSTableColumn *)aTableColumn
                         row:(NSInteger)rowIndex;
{
    NSObject *myObject = [[valuesController arrangedObjects] objectAtIndex:rowIndex];
    id columnIdentifier = [aTableColumn identifier];
    if ([columnIdentifier isEqual:@"foo"]) {
        if ([myObject respondsToSelector:@selector(fooValue)]) {
            return [myObject fooValue];
        }
    }
    return nil;
}

Note how I'm using the columns identifier to see which column is being requested. Set the columns identifier inside Interface Builder.

Daniel