7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swift:NSTableViewの項目をドラッグで入れ替えできるようにする

Last updated at Posted at 2019-02-02

概要

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
    }
    
}
7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?