By my reading of the docs and headers, NSTreeController uses NSIndexPaths to store selection. This means that its idea of selection is a chain of indexes into a tree of nested arrays. So as far as it knows, it is preserving the selection in the situation you describe. The problem here is the you're thinking of selection in terms of "object identity" and the tree controller defines selection as "a bunch of indexes into nested array". The behavior you describe is (AFAICT) the expected out-of-the-box behavior for NSTreeController.
If you want selection preservation by object identity, my suggestion would be to subclass NSTreeController and override all mutating methods such that you capture the current selection using -selectedNodes
before the mutation, then re-set the selection using -setSelectionIndexPaths:
with an array created by asking each formerly selected node for its new -indexPath
after the mutation.
In short, if you want behavior other than the stock behavior, you're going to have to write it yourself. I was curious how hard this would be so I took a stab at something that appears to work for the cases I bothered to test. Here 'tis:
@interface SOObjectIdentitySelectionTreeController : NSTreeController
@end
@implementation SOObjectIdentitySelectionTreeController
{
NSArray* mTempSelection;
}
- (void)dealloc
{
[mTempSelection release];
[super dealloc];
}
- (void)p_saveSelection
{
[mTempSelection release];
mTempSelection = [self.selectedNodes copy];
}
- (void)p_restoreSelection
{
NSMutableArray* array = [NSMutableArray array];
for (NSTreeNode* node in mTempSelection)
{
if (node.indexPath.length)
{
[array addObject: node.indexPath];
}
}
[self setSelectionIndexPaths: array];
}
- (void)insertObject:(id)object atArrangedObjectIndexPath:(NSIndexPath *)indexPath
{
[self p_saveSelection];
[super insertObject: object atArrangedObjectIndexPath: indexPath];
[self p_restoreSelection];
}
- (void)insertObjects:(NSArray *)objects atArrangedObjectIndexPaths:(NSArray *)indexPaths
{
[self p_saveSelection];
[super insertObjects:objects atArrangedObjectIndexPaths:indexPaths];
[self p_restoreSelection];
}
- (void)removeObjectAtArrangedObjectIndexPath:(NSIndexPath *)indexPath
{
[self p_saveSelection];
[super removeObjectAtArrangedObjectIndexPath:indexPath];
[self p_restoreSelection];
}
- (void)removeObjectsAtArrangedObjectIndexPaths:(NSArray *)indexPaths
{
[self p_saveSelection];
[super removeObjectsAtArrangedObjectIndexPaths:indexPaths];
[self p_restoreSelection];
}
@end
EDIT: It a little brutal (performance-wise) but I was able to get something working for calls to -setContent:
as well. Hope this helps:
- (NSTreeNode*)nodeOfObject: (id)object
{
NSMutableArray* stack = [NSMutableArray arrayWithObject: _rootNode];
while (stack.count)
{
NSTreeNode* node = stack.lastObject;
[stack removeLastObject];
if (node.representedObject == object)
return node;
[stack addObjectsFromArray: node.childNodes];
}
return nil;
}
- (void)setContent:(id)content
{
NSArray* selectedObjects = [[self.selectedObjects copy] autorelease];
[super setContent: content];
NSMutableArray* array = [NSMutableArray array];
for (id object in selectedObjects)
{
NSTreeNode* node = [self nodeOfObject: object];
if (node.indexPath.length)
{
[array addObject: node.indexPath];
}
}
[self setSelectionIndexPaths: array];
}
Of course, this relies on the objects actually being identical. I'm not sure what the guarantees are with respect to CoreData across your (unknown to me) background operation.