I think I've figured the basics out now. For the record, here's what I had to add to my view controller that already managed an NSOutlineView where I managed the datasource myself:
Create a class for the tree nodes:
@interface DataNode : NSObject {}
@property (retain) NSMutableArray *children;
@property (retain) NSString *firstText; // text for 1st column
@property (retain) NSString *secondText; // text for 2nd column
@end
@implementation DataNode
- (instancetype)init {
self.children = [NSMutableArray array];
return self;
}
- (BOOL) isLeaf {
return self.children.count == 0;
}
@end
Add these properties to your view controller:
@property (nonatomic, retain) NSTreeController *treeController;
@property (nonatomic, retain) NSMutableArray *treeContents;
Initialize the tree controller, e.g. from the view controller's awakeFromNib
:
self.treeContents = [NSMutableArray array]; // holds the add nodes
self.treeController = [[NSTreeController alloc] init];
[self.treeController setLeafKeyPath:@"isLeaf"]; // refers to DataNode
[self.treeController setChildrenKeyPath:@"children"]; // refers to DataNode
// set up the bindings
[self.treeController bind:@"contentArray" toObject:self withKeyPath:@"treeContents" options:@{NSRaisesForNotApplicableKeysBindingOption:@YES, NSConditionallySetsEditableBindingOption:@YES}];
[self.table bind:@"content" toObject:self.treeController withKeyPath:@"arrangedObjects" options:@{NSAlwaysPresentsApplicationModalAlertsBindingOption:@YES}];
[self.table bind:@"selectionIndexPaths" toObject:self.treeController withKeyPath:@"selectionIndexPaths" options:@{}];
[self.table bind:@"sortDescriptors" toObject:self.treeController withKeyPath:@"sortDescriptors" options:@{}];
Adding a node to the tree's root:
DataNode *node = [[DataNode alloc] init];
node.firstText = [NSString stringWithFormat:@"1 - %d", i1];
node.secondText = [NSString stringWithFormat:@"2 - %d", i1];
NSIndexPath *loc = [NSIndexPath indexPathWithIndex:self.contents.count]; // appends to end of list
[self.treeController insertObject:node atArrangedObjectIndexPath:loc];
Since my NSOutlineView is cell-based, I also had keep implementing the DataSource methods in order to provide the value for the cells, as I was not able to figure out how to make a binding for that:
-(NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
return 0; // never called (due to using NSTreeController)
}
-(id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
return nil; // never called (due to using NSTreeController)
}
-(BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
return NO; // never called (due to using NSTreeController)
}
-(id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
DataNode *node = [item representedObject];
return [node valueForKey:tableColumn.identifier];
}
The objectValueForTableColumn
method assumes that the table columns' identifiers have been set to firstText
and secondText
, respectively.