代码之家  ›  专栏  ›  技术社区  ›  JerguÅ¡ Lejko

键入NSTextField时筛选NSTable-自动选择第一行

  •  12
  • JerguÅ¡ Lejko  · 技术社区  · 7 年前

    我有一个 NSTextView 过滤a的字段 NSTable 表作为输入中的用户类型。我已经成功地实现了表过滤。

    现在,我的目标是自动选择第一个结果(表中的第一行),并允许用户在键入搜索查询时使用箭头键在结果之间移动。在表格中的结果之间移动时,输入字段应保持焦点。(这类似于聚光灯的工作方式)。

    这就是应用程序现在的样子:

    app

    这是我的 ViewController :

    import Cocoa
    
    class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate, NSTextFieldDelegate {
    
        @IBOutlet weak var field: NSTextField!
        @IBOutlet weak var table: NSTableView!
    
        var projects: [Project] = []
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            projects = Project.all()
    
            field.delegate = self
            table.dataSource = self
            table.delegate = self
        }
    
        override func controlTextDidChange(_ obj: Notification) {
            let query = (obj.object as! NSTextField).stringValue
    
            projects = Project.all().filter { $0.title.contains(query) }
    
            table.reloadData()
        }
    
        func numberOfRows(in tableView: NSTableView) -> Int {
            return projects.count
        }
    
        func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
            if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "FirstCell"), owner: nil) as? NSTableCellView {
                cell.textField?.stringValue = projects[row].title
                return cell
            }
    
            return nil
        }
    }
    

    这是 Project

    struct Project {
        var title: String = ""
    
        static func all() -> [Project] {
            return [
                Project(title: "first project"),
                Project(title: "second project"),
                Project(title: "third project"),
                Project(title: "fourth project"),
            ];
        }
    }
    

    非常感谢。

    2 回复  |  直到 7 年前
        1
  •  7
  •   Charles Srstka    7 年前

    这种情况下,@Willeke发布的副本中已经有一个答案,但1)答案是Objective-C,而不是Swift,2)我可以提供更详细的答案(带图片!),3)我厚颜无耻地追求赏金(收购规则#110)。因此,考虑到这一点,以下是我如何实现您的目标:

    不要使用 NSTextView ; 使用 NSTextField ,甚至更好 NSSearchField . NSSearchField 非常好,因为我们可以在Interface Builder中设置它来创建过滤器谓词,几乎不需要任何代码。我们要做的就是创建一个 NSPredicate 属性,然后将搜索字段的绑定检查器设置为指向它:

    enter image description here

    然后,您可以创建一个数组控制器,其过滤器谓词绑定到同一个属性 Content Array 绑定到视图控制器上的属性:

    enter image description here

    当然,还要将表视图绑定到阵列控制器:

    enter image description here

    最后,将表单元格视图中的文本字段绑定到 title 属性:

    enter image description here

    有了Interface Builder中的所有设置,我们几乎不需要任何代码。我们只需要定义 Project 类(需要标记所有属性 @objc 以便Cocoa绑定系统可以看到它们):

    class Project: NSObject {
        @objc let title: String
    
        init(title: String) {
            self.title = title
            super.init()
        }
    }
    

    我们还需要项目、数组控制器和过滤器谓词的视图控制器上的属性。筛选器谓词需要 dynamic 这样Cocoa绑定在更改和更新UI时可以得到通知。如果 projects 可以改变,做到这一点 动态 这样,对它的任何更改都会反映在UI中(否则,您可以 动态 就这么办吧 @objc let ).

    class ViewController: NSViewController {
        @IBOutlet var arrayController: NSArrayController!
    
        @objc dynamic var projects = [
            Project(title: "Foo"),
            Project(title: "Bar"),
            Project(title: "Baz"),
            Project(title: "Qux")
        ]
    
        @objc dynamic var filterPredicate: NSPredicate? = nil
    }
    

    最后但并非最不重要的是,视图控制器上的扩展符合 NSSearchFieldDelegate (或 NSTextFieldDelegate 如果您使用 NSTextField 而不是 NSSearchField ),我们将在其上实现 control(:textView:doCommandBy:) 方法基本上,我们拦截由搜索字段的字段编辑器执行的文本编辑命令 moveUp: moveDown: 回来 true 告诉字段编辑器我们将处理这些命令。对于除这两个选择器之外的所有内容,请返回 false 告诉字段编辑器执行通常的操作。

    请注意,这就是您应该使用 NSTextField NSSearchField 而不是 NSTextView ; 仅对调用此委托方法 NSControl 子类,其中 NSTextView 不是。

    extension ViewController: NSSearchFieldDelegate {
        func control(_: NSControl, textView _: NSTextView, doCommandBy selector: Selector) -> Bool {
            switch selector {
            case #selector(NSResponder.moveUp(_:)):
                self.arrayController.selectPrevious(self)
                return true
            case #selector(NSResponder.moveDown(_:)):
                self.arrayController.selectNext(self)
                return true
            default:
                return false
            }
        }
    }
    

    瞧!

    (当然,如果您喜欢手动填充表视图而不是使用绑定,那么可以忽略大部分内容,只需实现 控件(:textView:docomHandby:) ,手动更新表的选择,而不是要求阵列控制器进行更新。当然,使用绑定会产生漂亮、干净的代码,这就是我喜欢它的原因。)

        2
  •  1
  •   drootang    7 年前

    正如@Willeke指出的,这很可能是重复的。另一个问题的解决方案在这里有效。我已将其转换为swift并添加了一些解释。

    我用一个 NSSearchField 而不是 NSTextField ,但我希望它也能起到同样的作用。

    首先,您需要添加 NSControlTextEditingDelegate 您的协议 ViewController ,并添加以下功能:

    func control(_ control: NSControl, textView: NSTextView,
                 doCommandBy commandSelector: Selector) -> Bool {
        if commandSelector == #selector(moveUp(_:)) {
            table.keyDown(with: NSApp.currentEvent!)
            return true
        } else if commandSelector == #selector(moveDown(_:)) {
            table.keyDown(with: NSApp.currentEvent!)
            return true
        }
        return false
    }
    

    您已经将文本字段的委托设置为ViewController,所以您都在那里设置好了。

    这将导致 NSTextField 在执行之前首先检查委托 moveUp(_:) 选择器(按向上箭头触发)。在这里,函数的响应是“不要做您通常做的事情,委托将处理它”(返回 true )并将事件发送到 NSTableView 对象。文本字段上的焦点不会丢失。