过滤 NSOutlineView/NSTreeController

发布于 2024-07-27 19:23:24 字数 125 浏览 14 评论 0原文

如何使用搜索框来过滤 NSOutlineView/NSTreeController? 我知道这与绑定和谓词有关。 但没有具体说怎么做。 有人可以带我完成过滤 NSOutlineView/NSTreeController 的步骤吗?

How would I use a search box to filter a NSOutlineView/NSTreeController? I know it would have something to do with bindings and a predicate. But not specificaly how to. Could someone take me through the steps of filtering an NSOutlineView/NSTreeController?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

她比我温柔 2024-08-03 19:23:24

我认为你不能。 NSArrayController 允许你给它一个过滤谓词; NSTreeController 没有。 我建议您提交错误

I don't think you can. NSArrayController allows you to give it a filter predicate; NSTreeController doesn't. I suggest you file a bug.

情痴 2024-08-03 19:23:24

从 macOS 10.11 开始,NSTableView(以及子类 NSOutlineView)具有新的 hideRowsunhideRows 方法简化了过滤行的任务。 仍然没有自动支持过滤 NSTreeController 中的项目(它不是 NSArrayController 的子类,因此不会继承其 filter 谓词),但它至少做了很多繁重的工作,允许您将整个模型保留在控制器中,同时只显示它的一个子集。

As of macOS 10.11, NSTableView (and therefore the subclass NSOutlineView) has new hideRows & unhideRows methods that simplify the task to filtering out rows. There's still no automatic support for filtering out items in NSTreeController (which is not a subclass of NSArrayController, and thus does not inherit its filter predicate), but it at least does a lot of the heavy lifting of allowing you to keep the whole model in the controller while only displaying only a subset of it.

ぇ气 2024-08-03 19:23:24

我在这个问题上绞尽脑汁,但实时过滤 NSTreeController 实际上非常简单,您只需调整实际的节点对象即可。

就我而言,我放弃了在控制器上实现实际过滤器,只是将谓词传递到我的树节点上。 当谓词在叶节点上不匹配时,我只需将其从子数组中删除即可。

class PredicateOutlineNode: NSObject {

    typealias Element = PredicateOutlineNode

    @objc dynamic var children: [Element] = [] {
        didSet {
            propagatePredicatesAndRefilterChildren()
        }
    }

    @objc private(set) dynamic var filteredChildren: [Element] = [] {
        didSet {
            count = filteredChildren.count
            isLeaf = filteredChildren.isEmpty
        }
    }

    @objc private(set) dynamic var count: Int = 0
    @objc private(set) dynamic var isLeaf: Bool = true

    var predicate: NSPredicate? {
        didSet {
            propagatePredicatesAndRefilterChildren()
        }
    }

    private func propagatePredicatesAndRefilterChildren() {
        // Propagate the predicate down the child nodes in case either
        // the predicate or the children array changed.
        children.forEach { $0.predicate = predicate }

        // Determine the matching leaf nodes.
        let newChildren: [Element]

        if let predicate = predicate {
            newChildren = children.compactMap { child -> Element? in
                if child.isLeaf, !predicate.evaluate(with: child) {
                    return nil
                }
                return child
            }
        } else {
            newChildren = children
        }

        // Only actually update the children if the count varies.
        if newChildren.count != filteredChildren.count {
            filteredChildren = newChildren
        }
    }
}

现在,您可以将 NSTreeController 类名称设置为 PredicateOutlineNode 并将其关键路径设置为 filteredChildrencountisLeaf。 当然,您可以以不同的方式命名对象访问器。 现在,当我想要过滤树时,我在根节点上设置一个 NSPredicate ,将其向下传递。

也适用于 KVO,不需要额外的代码,NSOutlineView 将自动更新。

I wrecked my brain for quite a bit on this one, but its actually remarkably simple to live filter an NSTreeController, you just have to adapt your actual node object.

In my case I gave up on implementing the actual filter on the controller and just passed a predicate down my tree nodes. When the predicate doesn't match on a leaf node, I simply removes it from the children array.

class PredicateOutlineNode: NSObject {

    typealias Element = PredicateOutlineNode

    @objc dynamic var children: [Element] = [] {
        didSet {
            propagatePredicatesAndRefilterChildren()
        }
    }

    @objc private(set) dynamic var filteredChildren: [Element] = [] {
        didSet {
            count = filteredChildren.count
            isLeaf = filteredChildren.isEmpty
        }
    }

    @objc private(set) dynamic var count: Int = 0
    @objc private(set) dynamic var isLeaf: Bool = true

    var predicate: NSPredicate? {
        didSet {
            propagatePredicatesAndRefilterChildren()
        }
    }

    private func propagatePredicatesAndRefilterChildren() {
        // Propagate the predicate down the child nodes in case either
        // the predicate or the children array changed.
        children.forEach { $0.predicate = predicate }

        // Determine the matching leaf nodes.
        let newChildren: [Element]

        if let predicate = predicate {
            newChildren = children.compactMap { child -> Element? in
                if child.isLeaf, !predicate.evaluate(with: child) {
                    return nil
                }
                return child
            }
        } else {
            newChildren = children
        }

        // Only actually update the children if the count varies.
        if newChildren.count != filteredChildren.count {
            filteredChildren = newChildren
        }
    }
}

Now you can set your NSTreeController class name to PredicateOutlineNode and its key paths to filteredChildren, count and isLeaf. You can name your object accessors differently of course. Now when I want to filter the tree, I set an NSPredicate on the root node, which passes it down.

Also works with KVO, no additional code required, the NSOutlineView will update automatically.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文