我有一个NSTextView
字段,用于过滤NSTable
表作为输入中的用户类型。我已经成功实现了表过滤。
现在,我的目标是自动选择第一个结果(表格中的第一行),并允许用户在键入搜索查询时使用箭头键在结果之间移动。在表格中的结果之间移动时,输入字段应保持聚焦。 (这与Spotlight的工作方式类似)。
这就是应用程序现在的样子:
这是我的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"),
];
}
}
谢谢
答案 0 :(得分:4)
这种,sorta在@Willeke发布的副本中已有答案,但1)答案在Objective-C中,而不是Swift,2)我可以提供更详细的答案(带图片!),以及3)我肆无忌惮地追求赏金(收购规则#110)。所以,考虑到这一点,这就是我如何实现你想要做的事情:
不要使用NSTextView
;使用NSTextField
,甚至更好的NSSearchField
。 NSSearchField
很棒,因为我们可以在Interface Builder中设置它来创建几乎没有代码的过滤谓词。我们要做的就是在视图控制器中创建一个NSPredicate
属性,然后设置搜索字段的Bindings Inspector指向它:
然后你可以创建一个Array Controller,它的Filter Predicate绑定到同一个属性,并且它的Content Array
绑定绑定到视图控制器上的属性:
当然,将表视图绑定到数组控制器:
最后但并非最不重要的是,将表格单元格视图中的文本字段绑定到title
属性:
在Interface Builder中设置了所有内容,我们几乎不需要任何代码。我们需要的只是Project
类的定义(所有属性都需要标记为@objc
,以便Cocoa Bindings系统可以看到它们):
class Project: NSObject {
@objc let title: String
init(title: String) {
self.title = title
super.init()
}
}
我们还需要在视图控制器上为项目,数组控制器和过滤谓词提供属性。过滤谓词需要为dynamic
,以便可变绑定在更改和更新UI时得到通知。如果projects
可以更改,请同时设置dynamic
,以便对其进行任何更改都会反映在UI中(否则,您可以删除dynamic
并将其@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
不是。{/ p>
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
}
}
}
瞧!
(当然,如果你更喜欢手动填充表格视图而不是使用绑定,你可以忽略大部分内容而只是实现control(:textView:doCommandBy:)
,手动更新表的选择,而不是让你的阵列控制器去做当然,使用绑定会产生漂亮,干净的代码,这就是我喜欢它的原因。)
答案 1 :(得分:1)
@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
对象。焦点不会在文本字段中丢失。