I have an NSTreeController
bound an NSArrayController
bound to an entity and the tree bound to an NSOutlineView
. Now, when an "add" button is clicked, I would like to add a new entity to the treeController
, select the entity, and highlight it for editing. If I call [arrayController add]
, the insertion is asynchronous and I have no way of knowing which the new object is since the outline view does not select new rows automatically. So I am left with inserting the new Entity programatically. So addButton
calls createNewGroup
on a outlineViewController
(see below).
Again, inserting a new entity does not seem to be a synchronous process. I can't locate it in the NSOutlineView
in the next line after currentObject = [NSEntityDescription...
. And I did try after reloading the data. So I am left with observing the value changes in the array controller. This sort of works, most of the time, but occasionally it does not. Is this the right approach to this sort of thing?
- (void) createNewGroup:(id)sender {
NSInteger row = [myOutlineView selectedRow];
if(row == -1) {
[groupsController addObserver:self
forKeyPath:IR_GROUPS_KEYPATH
options:NSKeyValueObservingOptionInitial
context:IR_GROUPS_CONTEXT];
currentObject = [NSEntityDescription insertNewObjectForEntityForName:@"Group"
inManagedObjectContext:appDelegate.managedObjectContext];
return;
}
if([myOutlineView levelForRow:row] != 0) return;
[subGroupsController addObserver:self
forKeyPath:IR_GROUPS_KEYPATH
options:NSKeyValueObservingOptionInitial
context:IR_SUBGROUPS_CONTEXT];
NSManagedObject *parent = [[myOutlineView itemAtRow:row] representedObject];
currentObject = [NSEntityDescription insertNewObjectForEntityForName:@"Group"
inManagedObjectContext:appDelegate.managedObjectContext];
[currentObject setValue:parent forKey:@"parent"];
}
- (void) observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if([keyPath isEqualToString:IR_GROUPS_KEYPATH]) {
if(currentObject == nil) return;
[myOutlineView noteNumberOfRowsChanged];
NSString *ctx = (NSString *) context;
if([ctx isEqualToString:IR_GROUPS_CONTEXT]) {
NSInteger length = [myOutlineView numberOfRows];
NSInteger index;
for(index = 0; index < length; index++) {
id item = [myOutlineView itemAtRow:index];
if(currentObject == [item representedObject]) {
// We found the new object:
NSIndexSet *indices = [NSIndexSet indexSetWithIndex:index];
[myOutlineView selectRowIndexes:indices byExtendingSelection:NO];
[myOutlineView editColumn:0 row:index withEvent:nil select:YES];
currentObject = nil;
return;
}
}
//[groupsController removeObserver:self forKeyPath:nil];
} else if([ctx isEqualToString:IR_SUBGROUPS_CONTEXT]) {
NSTreeNode *parent = [myOutlineView itemAtRow:[myOutlineView selectedRow]];
[myOutlineView expandItem:parent];
NSInteger length = [myOutlineView numberOfRows];
NSInteger index;
for(index = 0; index < length; index++) {
id item = [myOutlineView itemAtRow:index];
if(currentObject == [item representedObject]) {
NSIndexSet *indices = [NSIndexSet indexSetWithIndex:index];
[myOutlineView selectRowIndexes:indices byExtendingSelection:NO];
[myOutlineView editColumn:0 row:index withEvent:nil select:YES];
currentObject = nil;
return;
}
}
}
}
}
If you use (a subclass of) NSTreeController to provide the content for you outlineView, it is very simple. You create a button, either in code or in Interface Builder, and set bind the target to insert:
to add an element or remove:
to delete it. In code it would look like this:
[aButton bind:NSTargetBinding
toObject:aController
withKeyPath:keyPathToTreeController
options:[NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSConditionallySetsEnabledBindingOption,
@"insert:", NSSelectorNameBindingOption,
nil]];
Selecting the new object is handled by the treeController. Again, in code:
[aTreeController setSelectsInsertedObjects:YES];
In IB, it's a checkbox that you need to check. Oh, there's also addChild:
. Let the bindings do their magic.