(注意:此方法可能不适用于具有大量行数的 tableView,因为我们正在遍历所有对象并设置新的 sortOrder)
总结这个问题 - 让 tableview 重新排序工作相对容易,这要归功于this 等有用的 SO 帖子 - 困难在于将此信息保存到 Core Data,因为表的用户/UI 重新排序被覆盖您绑定的 ArrayController 上的 sortDescriptors。绑定的 ArrayController 实质上撤消了用户的表行重新排序。这是我的工作代码:
我的数组控制器 sortDescriptors:
billablesArrayController.sortDescriptors = [NSSortDescriptor(key: "sortOrder", ascending: true)]
在我的 ViewController 的 onViewDidLoad 中:
override func viewDidLoad() {
super.viewDidLoad()
// Set ViewController as dataSource for tableView and register an array of accepted drag types
billablesTableView.dataSource = self
billablesTableView.registerForDraggedTypes([.string])
}
在 ViewController 中实现拖放方法:
extension JobsViewController: NSTableViewDataSource {
func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
let item = NSPasteboardItem()
item.setString(String(row), forType: .string)
return item
}
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
if dropOperation == .above {
// billablesArrayController.sortDescriptors are bound to tableView in xcode UI
// so we remove arrayController sortDescriptors temporarily so as not to mess with user/UI table reordering
billablesArrayController.sortDescriptors = []
return .move
}
return []
}
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
var oldIndexes = [Int]()
info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) { dragItem, _, _ in
if let str = (dragItem.item as! NSPasteboardItem).string(forType: .string), let index = Int(str) {
oldIndexes.append(index)
}
}
var oldIndexOffset = 0
var newIndexOffset = 0
var selectionIndex = 0
//Start tableView reordering
tableView.beginUpdates()
for oldIndex in oldIndexes {
if oldIndex < row {
tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
oldIndexOffset -= 1
selectionIndex = row - 1
} else {
tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
newIndexOffset += 1
selectionIndex = row
}
}
tableView.endUpdates()
//Get items.count from ArrayController for loop
if let items = billablesArrayController?.arrangedObjects as? [BillableItem] {
var newArray = [BillableItem]()
// get the new item order from the tableView
for i in 0..<items.count {
if let view = billablesTableView.view(atColumn: 0, row: i, makeIfNecessary: false) as? NSTableCellView {
if let tableItem = view.objectValue as? BillableItem {
newArray.append(tableItem)
}
}
}
// assign new sortOrder to each managedObject based on its index position in newArray
var index = 0
for bi in newArray {
bi.sortOrder = Int16(index)
index += 1
}
}
// reinstate arrayController sortDescriptors
billablesArrayController.sortDescriptors = [NSSortDescriptor(key: "sortOrder", ascending: true)]
// assign the dragged row as the selected item
billablesArrayController.setSelectionIndex(selectionIndex)
//save 'em
if managedObjectContext.hasChanges {
do {
try self.managedObjectContext.save()
} catch {
NSSound.beep()
_ = alertDialog(question: "Error: Can't save billable items sort order.", text: error.localizedDescription, showCancel: false)
}
}
return true
}
}