【问题标题】:Filtering a NSOutlineView/NSTreeController过滤一个 NSOutlineView/NSTreeController
【发布时间】:2009-07-25 10:23:21
【问题描述】:

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

【问题讨论】:

    标签: objective-c cocoa nsoutlineview


    【解决方案1】:

    我不认为你可以。 NSArrayController 允许你给它一个过滤谓词; NSTreeController 没有。我建议你file a bug

    【讨论】:

    • 我可以过滤 NSTreeController 连接到的 NSArrayController,但它会在 NSOutlineView 中显示结果吗?
    • 刚刚尝试提交错误,但登录时出现错误,显示了苹果错误报告者的能力。 snapplr.com/q3ns
    • 我的猜测是过滤数组控制器只会过滤根对象,而不是任何后代对象。我还没有尝试过,但我看不出它是如何工作的。
    • 至于bug记者:你有ADC账号吗?你需要一个。
    • 它有效,但只有根对象有没有办法扩展它来搜索后代对象?
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      在这个问题上我费了好大的劲儿,但实际上过滤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 将自动更新。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-20
        • 1970-01-01
        • 2012-04-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多