Last active
March 12, 2021 11:24
-
-
Save christopherjohst/ca30e5306dfb7bbd6c1cd64534224a57 to your computer and use it in GitHub Desktop.
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/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: ["[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]"]) | |
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks, resolve my problem.