我正在开发一个桌面 Cocoa 应用程序。在应用程序中,我有一个基于视图的 NSOutlineView 绑定到 NSTreeController:
NSTreeController 处于实体模式,由 Core Data 驱动。一切都按预期工作,直到底层模型图发生变化。每当一个新对象插入到已注册的 NSManagedObjectContext 中时,NSTreeController 都会刷新其内容,并且绑定的 NSOutlineView 会正确显示结果。控制器的内容使用 NSSortDescriptor 按“标题”排序,我在应用程序启动期间设置了此排序。唯一的缺点是即使在 NSTreeController 的首选项中选中了保留选择框, selectionIndexPath 也不会改变。我想保留在新节点出现在树中之前选择的对象上的选择。
我将 NSTreeController 子类化以调试在对象图更改期间选择发生的情况。我可以看到 NSTreeController 通过 KVO 更改了它的内容,但setContent:
没有调用该方法。比setSelectionIndexPaths:
通过 NSTreeControllerTreeNode KVO 调用但参数包含先前的 indexPath。
所以,要清楚:
- 顶级 1
- 文件夹 1-1
- 文件夹 1-2
- 顶级 2
- 文件夹 2-1
- *文件夹 2-3 <== 已选择
- 文件夹 2-4
在初始阶段选择“文件夹 2-3”。然后将“文件夹 2-2”插入到 NSManagedObjectContext 中[NSEntityDescription insertNewObjectForEntityForName:@"Folder" inManagedObjectContext:managedObjectContext];
:
- 顶级 1
- 文件夹 1-1
- 文件夹 1-2
- 顶级 2
- 文件夹 2-1
- *文件夹 2-2 <== 已选择
- 文件夹 2-3
- 文件夹 2-4
我想保留“文件夹 2-3”上的选择,因此我设置了“Preseve 选择”,但似乎 NSTreeController 完全忽略了这个属性,或者我误解了一些东西。
我怎样才能强制 NSTreeController 保持它的选择?
更新1:
不幸的是,我的 NSTreeController 子类中从未调用过任何突变方法(insertObject:atArrangedObjectIndexPath:
等)。insertObjects:atArrangedObjectIndexPaths:
我已经覆盖了大多数工厂方法来调试引擎盖下发生的事情,这就是当新的托管对象插入上下文时我可以看到的:
-[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 处于实体模式并绑定到 Application 委托的 managedObjectContext。我有一个名为“Folders”的根实体,它有一个名为“children”的属性。它与称为子文件夹的其他实体是一对多的关系。Subfolders 实体是 Folders 的子类,因此它具有与其父级相同的属性。正如您在第一个附加屏幕截图中看到的那样,NSTreeController 的实体已设置为文件夹实体,并且按预期工作。每当我将新的子文件夹插入 managedObjectContext 时,它就会出现在正确文件夹下的树中(作为子节点,由绑定到 NSTreeController 的 NSSortDescriptor 排序),
我可以看到该setContent:
方法在应用程序启动期间被调用,但仅此而已。似乎 NSTreeController 观察根节点(文件夹)并通过 KVO 以某种方式反映模型更改。(因此,当我创建一个新的子文件夹并将其添加到其父文件夹时,[folder addChildrenObject:subfolder]
它出现在树中,但没有调用任何树突变方法。)
不幸的是,我不能直接使用 NSTreeController 变异方法(add:
, addChild:
, insert:
, insertChild:
),因为真正的应用程序会在后台线程中更新模型。后台线程使用自己的 managedObjectContext 并将更改与mergeChangesFromContextDidSaveNotification
. 这让我抓狂,因为一切正常,期待 NSOutlineView 的选择。当我将一堆子文件夹从后台线程合并到主 managedObjectContext 时,树会自行更新,但是我丢失了在合并之前选择的对象的选择。
更新2:
我准备了一个小样本来演示这个问题:http ://cl.ly/3k371n0c250P
- 展开“文件夹 1”,然后选择“选择子文件夹 9999”
- 按“新建子文件夹”。它将在后台批量创建50个子文件夹。
- 如您所见,即使在 MyTreeController.m 中的内容更改之前保存了选择,“子文件夹 9999”中的选择也会丢失