You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

输入过滤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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:49:36