読者です 読者をやめる 読者になる 読者になる

NSTableViewの行ドラッグ&ドロップをやってみた

今回はNSTableView内の行をドラッグで移動させてみます。

やること

  1. ドラッグ元の行番号をPasteboardに保存する。
  2. ドロップされた時に、そのドロップを受け入れるかチェック。
  3. ドロップを受け入れたら、Pasteboardに保存していたドラッグ元の行番号を取り出して、ドロップ先と入れ替えるなど。

3では用途に応じて、移動元の行と移動先の行に対して自由に操作を行います。
今回は、移動元と移動先を「入れ替える」挙動にしてみました。

実装

NSTableViewDataSourceの以下3つを実装します。

1. ドラッグ元の行番号をPasteboardに保存する

func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
    // Pasteboardにドラッグ元の行情報を記録
    let data = NSKeyedArchiver.archivedData(withRootObject: rowIndexes)
    let item = NSPasteboardItem.init()
    item.setData(data, forType: draggingUTIType)
    pboard.writeObjects([item])
    return true
}

ペーストボードを使う所が少々トリッキーだなと感じました。

2. ドロップされた時に、そのドロップを受け入れるかチェック

func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
    // 今回は、同じTableViewからのDropのみを受け付ける
    guard let source = info.draggingSource() as? NSTableView, source === tableView else {
        return []
    }    
    return .move
}

例えば、別のウィンドウからファイルをドロップするような場合もここに入ります。 ファイルのドロップを受け入れたい時、または受け入れたくない時は、ここで許可や拒否をします。

  • .moveを返す:ドロップを受け入れる
  • []を返す:ドロップを受け入れない

NSDragOperation

今回は「移動」させたいのでNSDragOperation.moveを返しています。 NSDragOperationには、他にも.copyなどの操作があるので、移動以外にも使えるようです。(試していません)

3. ドロップを受け入れた後の処理

func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
    // ドラッグされたアイテムのUTI typeを確認
    guard let item = info.draggingPasteboard().pasteboardItems?.first?.data(forType: draggingUTIType) else {
        return false
    }
    // Pasteboardに記録しておいたドラッグ元の行Noを取り出し
    guard let sourceRowIndexes = NSKeyedUnarchiver.unarchiveObject(with: item) as? IndexSet, let from = sourceRowIndexes.first else {
        return false
    }

    // ... 入れ替えの処理など ...
    
    return true
}
  • ドラッグされたアイテムのUTIを確認して整合性チェック
  • Pasteboardに記録していたドラッグ元の行番号を取り出し

などしています。

ここではドロップ先の行番号が取得できるので、移動元と移動先の行番号を使ってデータの入れ替えなどを実装できます。

UTI

今回初めてUniform Type Identifiersの事を知りました。

サンプルではpublic.dataを指定しました。(誤っているかもしれません)

サンプル

以前にNSTableViewを試したサンプルに、ドラッグで行を入れ替えるコードを追加しました。

github.com

以上です!