NSTableView取消拖拽后,后续拖拽获取rowIndex异常为-1
NSTableView拖拽后再次拖拽时rowIndex返回-1的问题
问题现象
- 实现NSTableView内部拖拽功能正常,但将元素拖拽到表格边界外取消拖拽后,后续再次尝试拖拽时,
tableView(_:draggingSession:willBeginAt:forRowIndexes:)方法中通过屏幕点转换得到的rowIndex为-1,首次拖拽时能正常获取有效rowIndex,且表格尺寸和行数未发生变化。 - 拖拽到边界外时,
validateDrop返回空NSDragOperation,元素动画回到原位置,表格显示正常,但后续拖拽失效。
问题原因分析
当拖拽操作在表格外取消时,draggingDestinationFeedbackStyle被设置为.gap后没有正确重置——虽然在acceptDrop中重置了该属性,但拖拽取消时并不会调用acceptDrop,导致.gap状态被保留,干扰了后续拖拽会话的坐标转换逻辑,进而无法正确识别拖拽起始行。
解决方案
- 在拖拽会话结束的回调中,无论操作成功与否,强制重置表格的拖拽反馈样式为
.regular,确保状态回归正常。 - 直接使用拖拽会话回调中传入的
rowIndexes参数获取起始行索引,替代屏幕点转换的方式,避免坐标计算误差。
修改后的关键代码
1. 修正拖拽会话结束时的状态重置
public func tableView( _ tableView: NSTableView, draggingSession session: NSDraggingSession, endedAt screenPoint: NSPoint,operation: NSDragOperation) { // 无论拖拽成功与否,都重置拖拽反馈样式 tableView.draggingDestinationFeedbackStyle = .regular guard let windowPoint = tableView.window?.convertPoint(fromScreen: screenPoint) else { return } let tableViewPoint = tableView.convert(windowPoint, from: nil) let rowIndex = tableView.row(at: tableViewPoint) print("Drag Ended at row: \(rowIndex)") if !self.tableView.bounds.contains(tableViewPoint) { print("Dropped Outside") } }
2. 使用传入的rowIndexes替代坐标转换获取行索引
public func tableView(_ tableView: NSTableView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forRowIndexes rowIndexes: IndexSet) { // 直接使用参数中的rowIndexes获取行索引,无需坐标转换 guard let rowIndex = rowIndexes.first else { print("ERROR: No valid row index") return } let rows = self.tableView.numberOfRows print("DRAGGING WILL BEGIN AT row: \(rowIndex)") print("tableView has \(rows) rows -> bounds: \(self.tableView.bounds)") // 后续逻辑保持不变,使用rowIndex进行操作 guard let cellView = tableView.view(atColumn: 0, row: rowIndex, makeIfNecessary: true) else { print("ERROR: No cellView - 98264r") return } let xOffset = cellView.frame.origin.x session.enumerateDraggingItems(options: NSDraggingItemEnumerationOptions.concurrent, for: nil, classes: [NSPasteboardItem.self], searchOptions: [:]) { (item, index, _) in // ... 原有代码保持不变 } }
3. 优化validateDrop的判断逻辑(可选)
确保只有在拖拽源是当前表格且操作合法时才设置.gap,避免不必要的状态变更:
public func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation { guard dropOperation == .above, info.draggingSource as? NSTableView === self.tableView else { return [] } tableView.draggingDestinationFeedbackStyle = .gap return .move }
内容的提问来源于stack exchange,提问作者Marco
相关产品推荐
相关产品推荐

