Drag & Drop Reorder Rows on NSTableView

前端 未结 7 740
旧巷少年郎
旧巷少年郎 2020-12-08 11:21

I was just wondering if there was an easy way to set an NSTableView to allow it to reorder its rows without writing any pasteboard code. I only need it to be able to do thi

7条回答
  •  既然无缘
    2020-12-08 11:50

    Set your table view's datasource to be a class that conforms to NSTableViewDataSource.

    Put this in an appropriate place (-applicationWillFinishLaunching, -awakeFromNib, -viewDidLoad or something similar):

    tableView.registerForDraggedTypes(["public.data"])
    

    Then implement these three NSTableViewDataSource methods:

    tableView:pasteboardWriterForRow:
    tableView:validateDrop:proposedRow:proposedDropOperation:
    tableView:acceptDrop:row:dropOperation:
    

    Here is fully-working code that supports drag-and-drop reordering multiple rows:

    func tableView(tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
      let item = NSPasteboardItem()
      item.setString(String(row), forType: "public.data")
      return item
    }
    
    func tableView(tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
      if dropOperation == .Above {
        return .Move
      }
      return .None
    }
    
    func tableView(tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
      var oldIndexes = [Int]()
      info.enumerateDraggingItemsWithOptions([], forView: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) {
        if let str = ($0.0.item as! NSPasteboardItem).stringForType("public.data"), index = Int(str) {
                oldIndexes.append(index)
        }
      }
    
      var oldIndexOffset = 0
      var newIndexOffset = 0
    
      // For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly.
      // You may want to move rows in your content array and then call `tableView.reloadData()` instead.
      tableView.beginUpdates()
      for oldIndex in oldIndexes {
        if oldIndex < row {
          tableView.moveRowAtIndex(oldIndex + oldIndexOffset, toIndex: row - 1)
          --oldIndexOffset
        } else {
          tableView.moveRowAtIndex(oldIndex, toIndex: row + newIndexOffset)
          ++newIndexOffset
        }
      }
      tableView.endUpdates()
    
      return true
    }
    

    Swift 3 version:

    func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
        let item = NSPasteboardItem()
        item.setString(String(row), forType: "private.table-row")
        return item
    }
    
    func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
        if dropOperation == .above {
            return .move
        }
        return []
    }
    
    func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
        var oldIndexes = [Int]()
        info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) {
            if let str = ($0.0.item as! NSPasteboardItem).string(forType: "private.table-row"), let index = Int(str) {
                oldIndexes.append(index)
            }
        }
    
        var oldIndexOffset = 0
        var newIndexOffset = 0
    
        // For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly.
        // You may want to move rows in your content array and then call `tableView.reloadData()` instead.
        tableView.beginUpdates()
        for oldIndex in oldIndexes {
            if oldIndex < row {
                tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
                oldIndexOffset -= 1
            } else {
                tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
                newIndexOffset += 1
            }
        }
        tableView.endUpdates()
    
        return true
    }
    

提交回复
热议问题