NSOutlineView 检测何时折叠 children

NSOutlineView detect when children are collapsed

我正在尝试将项目的实际 collapsed/expanded 状态存储在 NSOutlineView 中,以便以后可以恢复。 NSOutlineViewDelegate 上有两种方法可用:

问题在于,这些方法不仅会针对用户单击的项目调用,还会针对可折叠的 children 调用。示例:

- a
-- b
--- c

a 折叠时 outlineViewItemDidCollapse 被调用两次,一次用于 b,一次用于 a。将两者标记为 collapsed 是不正确的,因为 b 仍应展开并且 c 在再次展开 a 后应该可见。所以 b 的实际状态应该是 expanded.

a 上的用户 Option-clicks 时,所有 children 也会折叠 (outlineView.collapseItem(item, collapseChildren: true))。再次展开 a 后,b 应保持折叠状态。在这种情况下,b 的状态应该是 collapsed

两种不同的状态:

有什么方法可以区分这两个actions/states,以便我以后可以正确恢复它吗?

编辑给出修改后的答案...

显然没有简单的方法来恢复给定容器在其父对象折叠后是展开还是折叠。显然,大纲视图的内部工作原理中的某些内容会被记住——可能就像存储单元格视图的显示按钮单元格的状态一样简单,或者它可能在树控制器或其节点中设置一个标志——但无论如何都没有直接编程接口。我怀疑您必须在模型对象中跟踪它。

为此,请将布尔值 属性 添加到您的模型项中,例如:

@property BOOL currentlyExpanded;

然后您需要实现两个委托方法 outlineViewItemDidExpand:outlineViewItemWillCollapse:,就像这样(假设您正在为大纲视图使用树控制器):

- (void)outlineViewItemDidExpand:(NSNotification *)notification {
    NSTreeNode * node = [notification.userInfo objectForKey:@"NSObject"];
    NSOutlineView * ov = notification.object;
    MyModelItem * item = [node representedObject];

    /*
      because we can only expand a visible container, we merely note
      that this container is now expanded in our view. This will be 
      called for every container that is expanded, so we don't have to 
      think about it much.
    */
    item.currentlyExpanded = YES;
}

- (void)outlineViewItemWillCollapse:(NSNotification *)notification {
    NSTreeNode * node = [notification.userInfo objectForKey:@"NSObject"];
    NSOutlineView * ov = notification.object;
    MyModelItem * item = [node representedObject];

    /* 
      Elements are collapsed from top to bottom. A collapsed parent 
      means the collapse started someplace farther up the chain than 
      our current item, so the expansion state of the current item is 
      not going to change unless the option key is held down, or you 
      implement a collapseItem:collapseChildren: with the second 
      parameter as YES. This accounts for the first; you'll have to 
      deal with the second in code.
    */

    BOOL optionKeyIsDown = [[NSApp currentEvent] modifierFlags] && NSEventModifierFlagOption;

    if ([ov isItemExpanded:[node parentNode]] || optionKeyIsDown) {
        item.currentlyExpanded = NO;
    }   
}

这些应该使模型项 属性 currentlyExpanded 与大纲视图的内部扩展 table(无论是什么)保持同步。如果您想引用它或将其存储在数据库中,您可以直接从模型对象访问它。

我处理位掩码的方式引发警告,但我懒得修复它...

编辑后保留这最后一部分,因为我认为这是很好的信息...

通常您不必担心这些; NSOutlineView 会自动 'do the right thing'。如果用户单击容器的显示三角形然后重新打开它,所有子容器将保持其 expanded/collapsed 状态;如果用户选择单击控制三角形,所有子容器将被标记为展开或折叠(取决于用户是选择打开还是选择关闭父容器)。除非你想要一些专门的行为(你通常会在委托方法 outlineView:shouldCollapseItem:outlineView:shouldExpandItem: 中设置),否则不要理会它。

如果您尝试在应用程序调用中保留展开状态,请将 NSOutlineView 属性 autosaveExpandedItems 设置为 true。无需簿记...

一些想法:

NSOutlineView可以保存和恢复展开的项目(autosaveExpandedItems)。可以从 NSUserDefaults 检索设置。关键是NSOutlineView Items <autosaveName>.

子类 NSOutlineView 并覆盖 expandItem(_:expandChildren:)collapseItem(_:collapseChildren:)。 children.

不调用这些方法

使用 outlineViewItemWillExpand(_:)outlineViewItemWillCollapse(_:) 中的当前事件可能可以找出展开或折叠的项目。