正在筛选NSOutlineView的内容



我已经用DataSource设置了一个NSOutlineView

提供给NSOutlineView的数据基本上是一个自定义节点树,每个节点(让我们称之为PPDocument)都有两个基本属性(还有更多,但这是最重要的部分):

  • 标签(显示内容)
  • 子节点(子节点数组)

当我的Filter字段(实际上是NSSearchField)更改时,我会在Outline视图中调用reloadData

因此,我决定将整个过滤插入数据源,如下所示:

- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(PPDocument*)doc {
    if (doc==nil) return [[[[APP documentManager] documentTree] groups] count]; // Root
    else
    {
        if ([[[APP fileOutlineFilter] stringValue] isEqualToString:@""]) // Unfiltered
            return [doc noOfChildren];
        else
            return [doc noOfChildrenFiltered:[[APP fileOutlineFilter] stringValue]];
    }
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(PPDocument*)doc {
    if (doc == nil) return [[[APP documentManager] documentTree] groups][index]; // Root
    else
    {
        if ([[[APP fileOutlineFilter] stringValue] isEqualToString:@""]) // Unfiltered
            return [doc childAtIndex:index];
        else
            return [doc childAtIndex:index filtered:[[APP fileOutlineFilter] stringValue]];
    }
}

以及3个主要的"过滤"功能:

- (NSArray*)filteredChildren:(NSString*)filter
{
    NSMutableArray* ret = [[NSMutableArray alloc] initWithObjects: nil];
    if (([self.label contains:filter]) && ([self.children count]==0)) return @[self];
    for (PPDocument* d in _children)
    {
        NSArray* filtered = [d filteredChildren:filter];
        if ([filtered count]>0)
        {
            PPDocument* newDoc = [d copy];
            newDoc.children = [filtered mutableCopy];
            [ret addObject:newDoc];
        }
    }
    return ret;
}
- (NSInteger)noOfChildrenFiltered:(NSString*)filter
{
    NSArray* filtered = [self filteredChildren:filter];
    return [filtered count];
}
- (PPDocument*)childAtIndex:(NSInteger)index filtered:(NSString*)filter {
    NSArray* filtered = [self filteredChildren:filter];
    return (PPDocument*)(filtered[index]);
}

然而,它似乎工作不正常(+isGroupItem:函数突然开始抛出EXC_BAD_ACCESS错误)。

有什么想法吗?你注意到有什么明显的错误吗?

您的-filteredChildren:方法对我来说似乎不正确。

首先,它永远不应该将自己作为其子对象之一返回(过滤或不过滤)。它似乎也不应该复制子节点。

我认为这应该有效:

- (NSArray*)filteredChildren:(NSString*)filter
{
    NSIndexSet* indexes = [_children indexesOfObjectsPassingTest:BOOL ^(PPDocument* child, NSUInteger idx, BOOL *stop){
        if (child.children.count)
            return [[child filteredChildren:filter] count] > 0;
        return [child.label contains:filter];
    }];
    return [_children objectsAtIndexes:indexes];
}

不过,这种方法的问题在于,您正在为项目的每个查询构建筛选子项的列表。NSOutlineView警告说,数据源方法将被频繁调用,并且必须是高效的。例如,它询问一个项目的子项的数量,然后您构建已筛选子项的数组,这就需要构建这些子项的已筛选子级的数组等,以确定子项是否应该存在,因为它有经过筛选的子项。然后它会问其中一个孩子有多少个孩子,你必须重建整个子树。

完成此操作后,我会让我的节点类跟踪持久数组中的子级和过滤后的子级。每个节点还必须跟踪当前过滤器。

一种方法是使它们始终保持同步。对children数组所做的任何更改也需要反映在已过滤的children中。也就是说,如果您添加了一个子项,并且它通过了筛选,则将其添加到相应位置的已筛选子项中。如果删除子级,也需要将其从筛选的子级数组中删除。

另一种方法是将过滤后的子数组视为缓存。对子数组的任何修改都会使该缓存失效。任何时候请求筛选的子数组时,如果无效,都会重新计算它。

无论哪种方式,当节点检测到其筛选的子数组已更改或可能已更改(即缓存已失效),从空变为非空,反之亦然时,它需要通知其父数组。这是因为它的空性会影响父对象是否将其保留在父对象的筛选子对象列表中。

在第一种方法中,过滤后的子数组是不断维护的,您需要一个方法来设置过滤器。这既应该更新当前节点的已筛选子级,也应该将新的筛选器传递给所有子级。在第二种方法中,最后使用的过滤器是缓存的一部分。当请求已筛选的子数组时,测试筛选器是否已更改。如果它已经失效,那么这相当于缓存已经失效,所以您可以重新计算它。

相关内容

  • 没有找到相关文章

最新更新