如何在没有NSTreeController的情况下选择NSOutlineView中的项目?

14
我正在使用NSOutlineView,并且没有使用NSTreeController,已经实现了自己的数据源。最好的方法是选择一个项目? NSOutlineView 已经支持 expandItem:collapseItem:。我缺少一个类似于`selectItem: `的方便方法。如何在程序中实现它?
谢谢。
6个回答

25

嗨Peter,谢谢你的回答。我已经知道selectRowIndexes:byExtendingSelection:方法了。问题是NSOutlineView使用NSIndexPath而不是NSIndexSet。 - cocoafan
在 NSOutlineView 的文档中,我没有看到任何 NSIndexPath 的实例。也许你想到的是 NSTreeController,但你并没有使用它。此外,一个大纲视图 就是 一个表格视图,这意味着所有表格视图功能都应该在大纲视图中正常工作。 - Peter Hosey
是的,我明白了。很遗憾没有内置的解决方案不依赖于我的数据源。我必须在我的数据源中编写额外的代码,对吧? - cocoafan
不需要更多步骤,只需按照这三个步骤即可选择特定的项目(或者多个项目)。 - Peter Hosey
我认为如果rowForItem:失败,我至少需要在我的数据源中的代码来展开节点。 - cocoafan
1
cocoafan:你在问题中提到了expandItem:。这是NSOutlineView的一个方法。我不确定你是否可以将其传递给一个深度嵌套的项目,但即使你不能,你也可以使用NSOutlineView的parentForItem:来构建所需项目祖先的堆栈,然后遍历该堆栈并依次展开每个项目。 - Peter Hosey

22

这就是我最终的方案。欢迎提出意见和纠正。

@implementation NSOutlineView (Additions)

- (void)expandParentsOfItem:(id)item {
    while (item != nil) {
        id parent = [self parentForItem: item];
        if (![self isExpandable: parent])
            break;
        if (![self isItemExpanded: parent])
            [self expandItem: parent];
        item = parent;
    }
}

- (void)selectItem:(id)item {
    NSInteger itemIndex = [self rowForItem:item];
    if (itemIndex < 0) {
        [self expandParentsOfItem: item];
        itemIndex = [self rowForItem:item];
        if (itemIndex < 0)
            return;
    }

    [self selectRowIndexes: [NSIndexSet indexSetWithIndex: itemIndex] byExtendingSelection: NO];
}
@end

2
谢谢这些方法。它们非常有用!在我看来,这个答案比之前被接受的更完整和详细。 - Ricardo Sanchez-Saez
这很棒 - 我们该如何在Swift中实现? - UKDataGeek
在问题中添加了一个 Swift 的答案。 - Ryan Francesconi

2
不,没有selectItem:方法,但是有一个rowForItem:方法。如果您将它与Peter关于使用selectRowIndexes:byExtendingSelection:的建议结合起来,您应该已经拥有所需的所有信息。
如果您真的想要一个选择项目的方法,出于一致性考虑,我建议称其为setSelectedItem:,您可以在NSOutlineView的类别中编写以下内容:
- (void)setSelectedItem:(id)item {
    NSInteger itemIndex = [self rowForItem:item];
    if (itemIndex < 0) {
        // You need to decide what happens if the item doesn't exist
        return;
    }

    [self selectRowIndexes:[NSIndexSet indexSetWithIndex:itemIndex] byExtendingSelection:NO];
}

我不知道这段代码是否有效;我只是匆忙写了它来说明概念。

感谢你们两位的建议。 - cocoafan

1

(这仍然是谷歌搜索结果中的热门内容)

Swift 4.2:

您需要确保先展开父级:

if let parent = outlineView.parent(myItem){
        mapOutlineView.expandItem(parent)
}


let rowIndex = outlineView.row(forItem: myItem)
        guard rowIndex != -1 else {return}
        outlineView.selectRowIndexes(IndexSet(integer: rowIndex), byExtendingSelection: false)

0
这是我用来以编程方式选择PXSourceList中项目的代码片段。
sourceList是一个常规的PXSouceList对象,我想选择大纲中第一组的第二个项目。
    NSInteger itemRow = [sourceList rowForItem:[[(SourceListItem *)[sourceListItems objectAtIndex:0] children] objectAtIndex:1]];
    [sourceList selectRowIndexes:[NSIndexSet indexSetWithIndex:itemRow] byExtendingSelection:YES];

如果您还不知道,PXSourceList是一个极好的替代方案,可以用来取代NSOutlineView,特别适合需要展示itunes/mail风格大纲的情况。可以在这里获得:PxSourceList


0
这是一个老问题,但情况仍然如此。由于有人要求使用Swift版本,这是我的想法。我发现接受的答案对我没有用,因为我认为你应该直接与数据源类交互,而不是扩展NSOutlineView。令人恼火的是,除非它们被展开,否则大纲不会找到行,这就是为什么使用您的数据源更简单。在我的情况下,我发现您必须按相反的顺序展开项目的父级,以便从顶层开始并朝着您要展开的实际项目工作。我觉得这应该内置,但除非我错过了什么-否则不是。

在这个例子中,FileItem是我的数据源集合项类。它包含一个属性“parent”,如果它是显示的层次结构的一部分,则需要有效。

func selectItem(_ item: FileItem, byExtendingSelection: Bool = false) {
    guard let outlineView = outlineView else { return }

    var itemIndex: Int = outlineView.row(forItem: item)

    if itemIndex < 0 {
        var parent: FileItem? = item

        var parents = [FileItem?]()
        while parent != nil {
            parents.append(parent)
            parent = parent?.parent
        }

        let reversedTree = parents.compactMap({$0}).reversed()

        for level in reversedTree {
            outlineView.expandItem(level, expandChildren: false)
        }

        itemIndex = outlineView.row(forItem: item)
        if itemIndex < 0 {
            print("Didn't find", item)
            return
        }
    }

    print("Expanding row", itemIndex)

    outlineView.selectRowIndexes(IndexSet(integer: itemIndex), byExtendingSelection: byExtendingSelection)
    outlineView.scrollRowToVisible(itemIndex)
}

我不同意。展开项目是视图的特征,应该由outlineView处理;它与模型无关。你只是在复制outlineView必须执行的代码。 - green_knight
你有一个如何做的例子吗? - Ryan Francesconi
请查看我上面发布的答案 - 它最终以没有索引计算的方式结束,或多或少地相同。 - green_knight

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接