我正在开发一个桌面可可应用程序。在这个应用程序中,我有一个基于视图的NSOutlineView绑定到一个NSTreeController:


NSTreeController处于实体模式,由核心数据驱动。一切按预期工作,直到下垫模型图发生变化。每当一个新对象插入到注册的NSManagedObjectContext中时,NSTreeController刷新其内容,绑定的NSOutlineView正确地显示结果。控制器的内容按照“标题”与NSSortDescriptor排序,我和我在应用程序启动期间设置了这个排序。唯一的缺点是,即使在NSTreeController的首选项中选中了保存选择框,selectionIndexPath也不会改变。我希望保留在新节点出现在树中之前选定的对象上的选择。
我对NSTreeController进行了子类化,以调试对象图更改期间所选内容发生的情况。我可以看到,NSTreeController通过KVO改变了它的内容,但是setContent:方法没有被调用。而不是通过setSelectionIndexPaths: KVO调用的NSTreeControllerTreeNode,但是参数包含前面的indexPath。

因此,要明确的是:
在初始阶段,选择了“文件夹2-3”。然后将“文件夹2-2”插入到NSManagedObjectContext中,并使用[NSEntityDescription insertNewObjectForEntityForName:@"Folder" inManagedObjectContext:managedObjectContext];。
我希望将选择保留在“文件夹2-3”上,因此我设置了“预选”,但是似乎NSTreeController完全忽略了这个属性,或者我误解了什么。
我怎样才能强迫NSTreeController保留它的选择?
UPDATE1:
不幸的是,没有任何一种突变方法(insertObject:atArrangedObjectIndexPath:,insertObjects:atArrangedObjectIndexPaths:等)。曾经调用过我的NSTreeController子类。我已经覆盖了大多数工厂方法来调试引擎盖下发生的事情,当一个新的托管对象插入到上下文中时,我可以看到这一点:
-[FoldersTreeController observeValueForKeyPath:ofObject:change:context:] // Content observer, registered with: [self addObserver:self forKeyPath:@"content" options:NSKeyValueObservingOptionNew context:nil]
-[FoldersTreeController setSelectionIndexPaths:]
-[FoldersTreeController selectedNodes]
-[FoldersTreeController selectedNodes]FoldersTreeController处于实体模式,并绑定到应用程序委托的managedObjectContext。我有一个名为“文件夹”的根实体,它有一个名为“子”的属性。它与另一个名为子文件夹的实体有很多关系。子文件夹实体是文件夹的子类,因此它具有与其父类相同的属性。正如您在第一个附加的屏幕截图中看到的那样,NSTreeController的实体已经设置为文件夹实体,并且它正在按预期工作。每当我将一个新的子文件夹插入到managedObjectContext中时,它就会出现在正确文件夹下的树中(作为子节点,按照绑定到NSTreeController的NSSortDescriptor排序),但是没有调用任何NSTreeController突变方法,如果新插入的子文件夹出现在列表的前面,它会删除所有内容,但所选内容保持在相同的位置。
我可以看到在应用程序启动期间调用了setContent:方法,但仅此而已。NSTreeController似乎通过KVO观察根节点(文件夹)并以某种方式反映模型更改。(因此,当我创建一个新的子文件夹并使用[folder addChildrenObject:subfolder]将其添加到其父文件夹时,它会出现在树中,但不会调用任何树突变方法。)
不幸的是,我不能直接使用NSTreeController突变方法(add:、addChild:、insert:、insertChild:),因为真正的应用程序在后台线程中更新模型。后台线程使用自己的managedObjectContext,并将更改分批合并到mergeChangesFromContextDidSaveNotification中。这让我抓狂,因为一切都很好,期待NSOutlineView的选择。当我从后台线程将许多子文件夹合并到主managedObjectContext中时,树会自我更新,但合并之前选择的对象的选择却丢失了。
Update2:
我准备了一个小示例来演示这个问题:http://cl.ly/3k371n0c250P
发布于 2013-05-03 11:05:56
通过读取文档和标头,NSTreeController使用NSIndexPaths存储选择。这意味着它的选择思想是一系列索引到嵌套数组树中。因此,据它所知,它在你描述的情况下保留了选择。这里的问题是从“对象标识”的角度考虑选择,树控制器将选择定义为“嵌套数组中的一组索引”。您描述的行为是NSTreeController预期的开箱即用行为.
如果您希望通过对象标识保存选择,我的建议是子类NSTreeController并覆盖所有的变异方法,以便在突变之前使用-selectedNodes捕获当前的选择,然后使用一个数组重新设置所选内容,该数组通过在突变后向每个以前选定的节点请求其新的-indexPath来创建。
简而言之,如果你想要的是股票行为以外的行为,你必须自己写。我很好奇这会有多难,所以我尝试了一些似乎对我费心测试的案例有用的东西。这是:
@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编辑:有点残忍(从性能上讲),但是我也可以为-setContent:调用找到一些有用的东西。希望这能帮上忙:
- (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];
}当然,这取决于对象实际上是相同的。我不确定在您的后台操作(我不知道)中,CoreData的保证是什么。
https://stackoverflow.com/questions/16356451
复制相似问题