输入过滤NSTableView时自动选中首行及键盘导航需求实现
实现Spotlight风格的搜索交互:自动选中首行+方向键切换保持输入框焦点
嘿,我懂你想要的那种丝滑Spotlight交互!咱们一步步修改代码,搞定这两个需求:
1. 自动选中过滤后的首行结果
在搜索内容变化、表格重载数据后,只要过滤结果不为空,就自动选中第一行,同时确保输入框不会失去焦点,让用户能继续输入。
2. 拦截方向键实现行切换,始终保持输入框焦点
默认按下方向键会让表格抢过焦点,咱们要拦截上下方向键,手动控制表格选中行,同时让输入框一直保持焦点,完全复刻Spotlight的体验。
修改后的完整ViewController代码
import Cocoa class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate, NSTextFieldDelegate { @IBOutlet weak var field: NSTextField! @IBOutlet weak var table: NSTableView! var projects: [Project] = [] // 跟踪当前选中行索引,方便方向键切换时快速定位 private var currentSelectedRow = -1 override func viewDidLoad() { super.viewDidLoad() projects = Project.all() field.delegate = self table.dataSource = self table.delegate = self // 初始加载时自动选中第一行(如果有数据) if !projects.isEmpty { table.selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false) currentSelectedRow = 0 } } override func controlTextDidChange(_ obj: Notification) { guard let textField = obj.object as? NSTextField else { return } let query = textField.stringValue // 改成不区分大小写的模糊搜索,体验更友好,不需要的话可以改回contains projects = Project.all().filter { $0.title.localizedCaseInsensitiveContains(query) } table.reloadData() // 过滤后自动选中首行,同时保持输入框焦点 if !projects.isEmpty { table.selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false) currentSelectedRow = 0 field.becomeFirstResponder() } else { table.deselectAll(nil) currentSelectedRow = -1 } } // 拦截方向键事件,自定义处理逻辑 override func keyDown(with event: NSEvent) { let keyCode = event.keyCode // 上下方向键的keyCode:上是126,下是125 switch keyCode { case 126: // 上方向键 if currentSelectedRow > 0 { currentSelectedRow -= 1 table.selectRowIndexes(IndexSet(integer: currentSelectedRow), byExtendingSelection: false) // 确保选中行在可视区域内 table.scrollRowToVisible(currentSelectedRow) } // 终止事件传递,不让焦点转移到表格 return case 125: // 下方向键 if currentSelectedRow < projects.count - 1 { currentSelectedRow += 1 table.selectRowIndexes(IndexSet(integer: currentSelectedRow), byExtendingSelection: false) table.scrollRowToVisible(currentSelectedRow) } // 终止事件传递,保持输入框焦点 return default: // 其他按键交给系统默认处理 super.keyDown(with: event) } } 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 } }
关键改动说明
currentSelectedRow变量:专门跟踪当前选中行,避免每次从表格读取,逻辑更清晰也更高效。- 过滤后自动选中逻辑:在表格重载后立即选中首行,并用
field.becomeFirstResponder()强制保持输入框焦点。 - 方向键拦截:重写
keyDown方法,捕获上下方向键后手动调整选中行,同时终止事件传递,不让表格抢走焦点;还加上了滚动到可视区域的逻辑,避免选中行在屏幕外。 - 初始选中处理:页面加载完成后如果有数据,也自动选中第一行,保持交互一致性。
内容的提问来源于stack exchange,提问作者Jerguš Lejko
相关产品推荐
相关产品推荐

