转载

[iOS] Cocoa:NSTableView 实现类似 Dock 的拖拽排序

本文将分享如何实现 Table View 的 Drag & Drop 排序,如何 Open Gap for Drag Destination 和实现过程中遇到的问题及相应解决方案。

Dock 拖拽排序效果(Open Gap for Drag Destination):

[iOS] Cocoa:NSTableView 实现类似 Dock 的拖拽排序

在 CurrencyX 1.3 版本中,我们增加了汇率列表可拖拽排序功能,实现过程很简单;在优化 Drop 效果为 Open Gap 时(即类似 Dock 上拖拽图标排序的效果)遇到了 Row 不断抖动的问题,最终偶然发现是因为仅设置了 rowHeight 属性而没有实现 Table View Delegate 中 Row Height 相关的方法导致的。

Drag & Drop

首先设置 Table View 可以接受的 Drag Type,在这里我们对 Currency 排序使用的是 Currency Code,Type 为 NSStringPboardType:

let registeredTypes:[String] = [NSStringPboardType]   tableView.registerForDraggedTypes(registeredTypes)

在用户 Drag 时,将需要的信息写到剪贴板上:

func tableView(tableView: NSTableView, writeRowsWithIndexes rowIndexes: NSIndexSet, toPasteboard pboard: NSPasteboard) -> Bool {       let currencyCode = // Get Currency Code At Row     let registeredTypes:[String] = [NSStringPboardType]     pboard.declareTypes(registeredTypes, owner: self)     pboard.setString(currencyCode, forType: NSStringPboardType)     return true }

在 Drop 时,首先根据用户拖拽 Row 的位置来返回适当的操作,在这里,我们希望仅当用户插入两行之间时执行 Move 操作:

func tableView(tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {       if (dropOperation == .Above) { return .Move }     return .None; }

对于 Valid Drop 操作,通过实现下述方法获取剪贴板上的信息并依次进行数据排序,更新 Table View:

func tableView(tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {       if let currencyCode = info.draggingPasteboard().stringForType(NSStringPboardType),        let oldIndex = userCurrencyCodes.indexOf(currencyCode) {         // Update Data Source         // Update Table View         return true     } else {         return false     } }

至此,我们已经完成了 Table View 的 Drag & Drop 排序。

[iOS] Cocoa:NSTableView 实现类似 Dock 的拖拽排序

Open Gap

同时我们也正在开发 Today Extension,发现系统自带的排序操作起来用户体验更好一些。

[iOS] Cocoa:NSTableView 实现类似 Dock 的拖拽排序

我们也决定使用这样的 Drop 效果,实现起来也很简单,只需要对 Table View 进行如下设置即可:

tableView.draggingDestinationFeedbackStyle = .Gap

然而运行起来得到的结果是可怕的:

[iOS] Cocoa:NSTableView 实现类似 Dock 的拖拽排序

Debug

首先我们新建了一个测试工程,创建了一个最简单的 Table View 并将 Drag & Drop 相关的代码加入其中,发现仍然会出现拖拽时整个 View 抖动或者某些 Row 弹跳的问题。确定引发问题的原因与我们自定义的 NSTableCellView 没有关系。

接着仔细察看文档和 Google 到如何实现“Open Gap for Drag & Drop”的相关文章,我们的实现似乎并没有问题。

在 Google 时发现 StackOverflow 上有人提到 NSTableView 的 Dragging Destination Feedback Style 是 OS X 10.9 Mavericks 之后才提供的 API,并 给出了针对更早系统自定义的实现方式 。基本思路是在用户 Drag 到某个 Row 上时,使用自定义 Table View 的 noteHeightOfRowsWithIndexesChanged 方法修改 Row Height 来实现的。猜测系统 API 的实现方式应该也无太大区别。

联想到我们的代码中,仅在 Table View 初始化时设置了 rowHeight 属性,拖拽时抖动的表现可能是 Table View 无法获取到 Row 正确的高度。于是实现了 Delegate 方法返回 Row Height:

func tableView(tableView: NSTableView, heightOfRow row: Int) -> CGFloat

再次运行便成功了:tada:。

[iOS] Cocoa:NSTableView 实现类似 Dock 的拖拽排序

此外,在 Drop Destination Feedback Style 为 Gap 时,为了获得更好的用户体验,可以修改 Validate Drop 函数在 dropOperation 为 .Above 或 .On 的时候都 return .Move ,这样也和 Extension 中系统的默认行为更加接近。

Happy Coding :beer:

顺便看了一眼毫无进度的某下载软件,拖拽时也有些颤~抖。

[iOS] Cocoa:NSTableView 实现类似 Dock 的拖拽排序

支持我们

SalesX 是给 Apple 开发者使用的菜单栏工具,第一时间把 app 销售情况推送给你,7 天免费试用

CurrencyX 是 Mac 上小而美的汇率 app

如果你觉得文章对你有帮助,可以买一个支持我们

关注我们公众号,获取最新文章推送 [iOS] Cocoa:NSTableView 实现类似 Dock 的拖拽排序

原文  http://blog.seedlab.io/open-gap-for-nstableview-drag-and-drop/
正文到此结束
Loading...