-
-
Save wookiee/e25baec42816b129290c4c7123cdad2a to your computer and use it in GitHub Desktop.
NSTableView reordering row with drag and drop
This file contains hidden or 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
// | |
// ViewController.swift | |
// DragTable | |
// | |
// Created by Anna Kim on 2017. 2. 9.. | |
// Copyright © 2017년 Anna Kim. All rights reserved. | |
// | |
import Cocoa | |
// MARK: - Array Extension | |
// 원소의 위치 이동을 위한 Array 타입 확장 | |
extension Array { | |
mutating func move(from start: Index, to end: Index) { | |
guard (0..<count) ~= start, (0...count) ~= end else { return } | |
if start == end { return } | |
let targetIndex = start < end ? end - 1 : end | |
insert(remove(at: start), at: targetIndex) | |
} | |
mutating func move(with indexes: IndexSet, to toIndex: Index) { | |
let movingData = indexes.map{ self[$0] } | |
let targetIndex = toIndex - indexes.filter{ $0 < toIndex }.count | |
for (i, e) in indexes.enumerated() { | |
remove(at: e - i) | |
} | |
insert(contentsOf: movingData, at: targetIndex) | |
} | |
} | |
// MARK: - View Controller Implementation | |
class ViewController: NSViewController { | |
var names = ["apple", "banana", "cherry", "orange", "pear", "peach" ] | |
@IBOutlet weak var sourceTableView: NSTableView! | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
sourceTableView.register(forDraggedTypes: ["public.data"]) | |
sourceTableView.allowsMultipleSelection = true | |
} | |
} | |
// MARK: - NSTableViewDataSource, NSTableViewDelegate | |
extension ViewController: NSTableViewDelegate, NSTableViewDataSource { | |
func numberOfRows(in tableView: NSTableView) -> Int { | |
return names.count | |
} | |
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? { | |
return names[row] | |
} | |
// MARK: Allow Drag Operation | |
/* tableView(_:writeRowsWith:to:) 를 구현하면서 true를 리턴하게 하면 해당 테이블 뷰에서 드래그가 가능해진다. | |
이때, 실제 데이터보다는 선택된 셀들의 인덱스들을 인코딩해버리는 것이 편하다. | |
*/ | |
func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool { | |
let data = NSKeyedArchiver.archivedData(withRootObject: rowIndexes) | |
let item = NSPasteboardItem() | |
item.setData(data, forType: "public.data") | |
pboard.writeObjects([item]) | |
return true | |
} | |
// MARK: Drag Destination Actions | |
func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation { | |
guard let source = info.draggingSource() as? NSTableView, | |
source === sourceTableView | |
else { return [] } | |
if dropOperation == .above { | |
return .move | |
} | |
return [] | |
} | |
func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool { | |
let pb = info.draggingPasteboard() | |
if let itemData = pb.pasteboardItems?.first?.data(forType: "public.data"), | |
let indexes = NSKeyedUnarchiver.unarchiveObject(with: itemData) as? IndexSet | |
{ | |
names.move(with: indexes, to: row) | |
let targetIndex = row - (indexes.filter{ $0 < row }.count) | |
// 드래그했던 셀들을 다시 선택 상태로 만들어 둔다. | |
tableView.selectRowIndexes(IndexSet(targetIndex..<targetIndex+indexes.count), byExtendingSelection: false) | |
return true | |
} | |
return false | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment