Swift中如何实时检测NSTableView列拖动与调整大小?
解决NSTableView列拖动/调整大小时实时更新排序箭头位置的问题
要实现列拖动或调整大小过程中实时更新排序箭头的位置,你可以通过以下两种核心方案结合来覆盖所有场景:
1. 子类化NSTableHeaderView监听拖动事件
表头的列拖动、列宽调整操作均由NSTableHeaderView处理,重写它的mouseDragged(with:)方法可以在拖动过程中持续触发更新逻辑:
class CustomTableHeaderView: NSTableHeaderView { // 定义闭包传递更新信号 var updateSortArrow: (() -> Void)? override func mouseDragged(with event: NSEvent) { super.mouseDragged(with: event) // 拖动过程中实时调用更新逻辑 updateSortArrow?() } }
在表格视图控制器中替换默认表头并绑定更新逻辑:
override func viewDidLoad() { super.viewDidLoad() let customHeader = CustomTableHeaderView(frame: tableView.headerView!.frame) customHeader.updateSortArrow = { [weak self] in self?.updateSortArrowPosition() } tableView.headerView = customHeader } // 自定义箭头位置更新逻辑 private func updateSortArrowPosition() { guard let sortedColumnIndex = tableView.sortedColumn else { return } // 获取当前排序列的表头实时frame let headerCellFrame = tableView.headerView?.frameOfColumn(sortedColumnIndex) ?? .zero // 示例:将箭头靠右对齐(可根据需求调整位置参数) sortArrowView.frame = CGRect( x: headerCellFrame.maxX - 20, y: headerCellFrame.midY - 8, width: 16, height: 16 ) }
2. KVO监听列宽变化(覆盖非鼠标操作场景)
对于键盘调整列宽(如Option+左右箭头)这类不触发鼠标拖动的场景,通过KVO监听NSTableColumn的width属性实现实时更新:
override func viewDidLoad() { super.viewDidLoad() // 为现有列添加KVO监听 tableView.tableColumns.forEach { column in column.addObserver(self, forKeyPath: #keyPath(NSTableColumn.width), options: .new, context: nil) } // 监听列数组变化,确保新增列也能被监听 tableView.addObserver(self, forKeyPath: #keyPath(NSTableView.tableColumns), options: .new, context: nil) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { switch keyPath { case #keyPath(NSTableColumn.width), #keyPath(NSTableView.tableColumns): updateSortArrowPosition() default: super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } } deinit { // 移除所有KVO观察者,避免内存泄漏 tableView.tableColumns.forEach { column in column.removeObserver(self, forKeyPath: #keyPath(NSTableColumn.width)) } tableView.removeObserver(self, forKeyPath: #keyPath(NSTableView.tableColumns)) }
补充说明
- 两种方案结合可覆盖鼠标拖动列、鼠标调整列宽、键盘调整列宽等所有场景;
- 使用
tableView.headerView?.frameOfColumn(_:)能准确获取排序列表头的实时frame,以此计算箭头位置更可靠; - 若箭头是表头单元格的子视图,可在自定义
NSTableHeaderCell中重写drawInterior(with:in:)方法,结合上述触发逻辑实时重绘箭头。
内容的提问来源于stack exchange,提问作者ballade4op52
相关产品推荐
相关产品推荐

