21

NSTableView を設定して、ペーストボード コードを記述せずに行を並べ替える簡単な方法があるかどうか疑問に思っていました。1 つのテーブル内でこれを内部的に実行できるようにするためだけに必要です。pboard コードを書くのに問題はありませんが、Interface Builder がどこかにトグルを持っているのを見た/デフォルトで動作しているのを見たことを除いては。それは確かに十分に一般的なタスクのようです。

ありがとう

4

9 に答える 9

32

テーブル ビューのデータ ソースを に準拠するクラスに設定しますNSTableViewDataSource

これを適切な場所 ( -applicationWillFinishLaunching-awakeFromNib-viewDidLoadまたは同様のもの) に配置します。

tableView.registerForDraggedTypes(["public.data"])

NSTableViewDataSource次に、次の 3 つのメソッドを実装します。

tableView:pasteboardWriterForRow:
tableView:validateDrop:proposedRow:proposedDropOperation:
tableView:acceptDrop:row:dropOperation:

以下は、ドラッグ アンド ドロップによる複数行の並べ替えをサポートする、完全に機能するコードです。

func tableView(tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
  let item = NSPasteboardItem()
  item.setString(String(row), forType: "public.data")
  return item
}

func tableView(tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
  if dropOperation == .Above {
    return .Move
  }
  return .None
}

func tableView(tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
  var oldIndexes = [Int]()
  info.enumerateDraggingItemsWithOptions([], forView: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) {
    if let str = ($0.0.item as! NSPasteboardItem).stringForType("public.data"), index = Int(str) {
            oldIndexes.append(index)
    }
  }

  var oldIndexOffset = 0
  var newIndexOffset = 0

  // For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly.
  // You may want to move rows in your content array and then call `tableView.reloadData()` instead.
  tableView.beginUpdates()
  for oldIndex in oldIndexes {
    if oldIndex < row {
      tableView.moveRowAtIndex(oldIndex + oldIndexOffset, toIndex: row - 1)
      --oldIndexOffset
    } else {
      tableView.moveRowAtIndex(oldIndex, toIndex: row + newIndexOffset)
      ++newIndexOffset
    }
  }
  tableView.endUpdates()

  return true
}

スウィフト 3バージョン:

func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
    let item = NSPasteboardItem()
    item.setString(String(row), forType: "private.table-row")
    return item
}

func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
    if dropOperation == .above {
        return .move
    }
    return []
}

func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
    var oldIndexes = [Int]()
    info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) {
        if let str = ($0.0.item as! NSPasteboardItem).string(forType: "private.table-row"), let index = Int(str) {
            oldIndexes.append(index)
        }
    }

    var oldIndexOffset = 0
    var newIndexOffset = 0

    // For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly.
    // You may want to move rows in your content array and then call `tableView.reloadData()` instead.
    tableView.beginUpdates()
    for oldIndex in oldIndexes {
        if oldIndex < row {
            tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
            oldIndexOffset -= 1
        } else {
            tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
            newIndexOffset += 1
        }
    }
    tableView.endUpdates()

    return true
}
于 2014-11-11T00:27:00.273 に答える
16

IB のツール ヒントを見ると、参照しているオプションが

- (BOOL)allowsColumnReordering

コントロール、まあ、列の並べ替え。テーブル ビューの標準のドラッグ アンド ドロップ API 以外にこれを行う方法はないと思います。

編集: ( 2012-11-25 )

答えは、のドラッグアンドドロップの並べ替えに関するものNSTableViewColumnsです。そして、それが当時受け入れられていた答えでした。ほぼ 3 年が経過した今、正しいとは思えません。情報を検索者にとって役立つものにするために、より正確な回答を提供するように努めます。

NSTableViewInterface Builderで行のドラッグ アンド ドロップによる並べ替えを可能にする設定はありません。NSTableViewDataSource次のような特定のメソッドを実装する必要があります。

- tableView:acceptDrop:row:dropOperation:

- (NSDragOperation)tableView:(NSTableView *)aTableView validateDrop:(id < NSDraggingInfo >)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation

- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard

これを含め、これを合理的に徹底的に解決する他の SO の質問があります。

ドラッグ アンド ドロップ APIへの Apple リンク。

于 2010-01-25T21:59:12.777 に答える
4

残念ながら、ペースト ボード コードを記述する必要があります。ドラッグ アンド ドロップ API は非常に汎用的であるため、非常に柔軟です。ただし、並べ替えが必要なだけの場合は、少しやり過ぎです。とにかく、アイテムを追加および削除したり、並べ替えたりできる小さなサンプル プロジェクトを作成しました。NSOutlineView

これはNSTableView違いますが、ドラッグ アンド ドロップ プロトコルの実装は基本的に同じです。

ドラッグ アンド ドロップを一度に実装したので、このコミットを参照することをお勧めします。

スクリーンショット

于 2013-10-24T08:03:49.980 に答える
3

一度に 1 行だけ移動する場合は、次のコードを使用できます。

    func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
        let pasteboard = info.draggingPasteboard()
        guard let pasteboardData = pasteboard.data(forType: basicTableViewDragAndDropDataType) else { return false }
        guard let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: pasteboardData) as? IndexSet else { return false }
        guard let oldIndex = rowIndexes.first else { return false }

        let newIndex = oldIndex < row ? row - 1 : row
        tableView.moveRow(at: oldIndex, to: newIndex)

        // Dont' forget to update model

        return true
    }
于 2017-01-06T15:59:59.420 に答える
1

これは、Swift 3に対する@Ethan の回答の更新です。

let dragDropTypeId = "public.data" // or any other UTI you want/need

func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {
    let item = NSPasteboardItem()
    item.setString(String(row), forType: dragDropTypeId)
    return item
}

func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
    if dropOperation == .above {
        return .move
    }
    return []
}

func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableViewDropOperation) -> Bool {
    var oldIndexes = [Int]()
    info.enumerateDraggingItems(options: [], for: tableView, classes: [NSPasteboardItem.self], searchOptions: [:]) {
        if let str = ($0.0.item as! NSPasteboardItem).string(forType: self.dragDropTypeId), let index = Int(str) {
            oldIndexes.append(index)
        }
    }

    var oldIndexOffset = 0
    var newIndexOffset = 0

    // For simplicity, the code below uses `tableView.moveRowAtIndex` to move rows around directly.
    // You may want to move rows in your content array and then call `tableView.reloadData()` instead.
    tableView.beginUpdates()
    for oldIndex in oldIndexes {
        if oldIndex < row {
            tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
            oldIndexOffset -= 1
        } else {
            tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
            newIndexOffset += 1
        }
    }
    tableView.endUpdates()

    self.reloadDataIntoArrayController()

    return true
}
于 2016-10-06T19:56:45.097 に答える