概要
TableView
の項目をドラッグで入れ替えられるようにしようと思ったら、MacかつMojave以降だとかなり厄介かつTipsが全然ないことに気づきました。忘備録!
ソース
下準備
class ViewController: NSViewController {
@IBOutlet weak var myTableView: NSTableView!
@IBOutlet weak var segmentedControl: NSSegmentedControl!
private var data = [String]()
override func viewDidLoad() {
super.viewDidLoad()
myTableView.delegate = self
myTableView.dataSource = self
// ↓これがドラッグを可能にする
myTableView.registerForDraggedTypes([NSPasteboard.PasteboardType("public.data")])
data.append("Apple")
data.append("Banana")
data.append("Grape")
data.append("Peach")
myTableView.reloadData()
segmentedControl.setEnabled(false, forSegment: 0)
}
@IBAction func removeData(_ sender: Any) {
data.remove(at: myTableView.selectedRow)
myTableView.reloadData()
segmentedControl.setEnabled(false, forSegment: 0)
}
}
本実装
extension ViewController: NSTableViewDelegate, NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return data.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
// ここのIdentifierの指定の仕方要注意 StoryBoardでNSTableCellViewに対して設定する必要があります。
let cell = myTableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "DataCell"), owner: self) as? NSTableCellView
cell?.textField?.stringValue = data[row]
return cell
}
func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
segmentedControl.setEnabled(true, forSegment: 0)
return true
}
func tableViewSelectionDidChange(_ notification: Notification) {
for i in (0 ..< data.count) {
if myTableView.isRowSelected(i) {
return
}
}
segmentedControl.setEnabled(false, forSegment: 0)
}
// ここから下がドラッグ機能に必要
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {
if dropOperation == .above {
return .move
}
return []
}
func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
do {
// Mojaveから扱いが難しくなった部分,Tipsほぼ0
let data = try NSKeyedArchiver.archivedData(withRootObject: rowIndexes, requiringSecureCoding: false)
pboard.declareTypes([NSPasteboard.PasteboardType("public.data")], owner: self)
pboard.setData(data, forType: NSPasteboard.PasteboardType("public.data"))
} catch {
Swift.print(error)
}
return true
}
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {
let pboard = info.draggingPasteboard
if let pboardData = pboard.data(forType: NSPasteboard.PasteboardType("public.data")) {
do {
if let rowIndexes = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(pboardData) as? IndexSet {
myTableView.beginUpdates()
for oldRow in rowIndexes {
if oldRow < row {
myTableView.moveRow(at: oldRow, to: row - 1)
data.swapAt(oldRow, row - 1)
} else if row < data.count {
myTableView.moveRow(at: oldRow, to: row)
data.swapAt(oldRow, row)
}
}
myTableView.endUpdates()
return true
}
} catch {
Swift.print(error)
}
}
return false
}
}