Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Programmatically reordering a table view on macOS using drag and drop. A full example in this Playground, with setup and tear down code. For a detailed guide to how this works, see: https://kitcross.net/reorder-table-views-drag-drop/
import Cocoa
import PlaygroundSupport
class BackgroundView: NSView {
override func draw(_ dirtyRect: NSRect) {
NSColor.underPageBackgroundColor.set()
dirtyRect.fill()
}
}
class ReorderTableViewController: NSViewController {
// Our data source. It’s important this is an array
// so we can rearrange it later on after a drop
var accounts: [String] = {
var array: [String] = []
array.append(contentsOf: ["erlich@piedpiper.com", "jared@piedpiper.com", "bertram@piedpiper.com", "dinesh@piedpiper.com", "nelson@piedpiper.com"])
return array
}()
let tableView: NSTableView = {
let tableView = NSTableView()
tableView.usesAlternatingRowBackgroundColors = true
tableView.allowsColumnReordering = false
tableView.allowsColumnResizing = false
return tableView
}()
let scrollView = NSScrollView()
override func loadView() {
view = BackgroundView()
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerForDraggedTypes([.string])
let emailColumn = NSTableColumn()
emailColumn.title = "E-mail address"
emailColumn.width = 230
tableView.delegate = self
tableView.dataSource = self
tableView.addTableColumn(emailColumn)
scrollView.documentView = tableView
scrollView.frame = NSRect(x: 20, y: 20, width: 400, height: 200)
scrollView.borderType = .lineBorder
view.addSubview(scrollView)
}
}
extension ReorderTableViewController: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
return 30
}
}
extension ReorderTableViewController: NSTableViewDataSource {
func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
return accounts[row] as NSString
}
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
guard dropOperation == .above,
let tableView = info.draggingSource as? NSTableView else { return [] }
tableView.draggingDestinationFeedbackStyle = .gap
return .move
}
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
/* Read the pasteboard items and ensure there is at least one item,
find the string of the first pasteboard item and search the datasource
for the index of the matching string */
guard let items = info.draggingPasteboard.pasteboardItems,
let pasteBoardItem = items.first,
let pasteBoardItemName = pasteBoardItem.string(forType: .string),
let index = accounts.firstIndex(of: pasteBoardItemName) else { return false }
let indexset = IndexSet(integer: index)
accounts.move(fromOffsets: indexset, toOffset: row)
/* Animate the move to the rows in the table view. The ternary operator
is needed because dragging a row downwards means the row number is 1 less */
tableView.beginUpdates()
tableView.moveRow(at: index, to: (index < row ? row - 1 : row))
tableView.endUpdates()
return true
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let view = NSView()
let account = accounts[row]
let emailTextField = NSTextField(labelWithString: account)
emailTextField.frame = NSRect(x: 0, y: 7, width: 200, height: 16)
view.addSubview(emailTextField)
return view
}
func numberOfRows(in tableView: NSTableView) -> Int {
return accounts.count
}
}
let vc = ReorderTableViewController(nibName: nil, bundle: nil)
vc.view.frame = NSRect(x: 0, y: 0, width: 440, height: 240)
PlaygroundPage.current.liveView = vc
@funway

This comment has been minimized.

Copy link

@funway funway commented Oct 9, 2020

Thanks, resolve my problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.