NSTableview - Drag and Drop split into two classes

2019-09-15 10:21发布

i found this helpfully tutorial for realize drag an drop with nstabelview: https://drive.google.com/open?id=0B8PBtMQt9GdONzV3emZGQWUtdmM

this works fine.

but i would like to split both table views into differente view controllers and classes with a split view:

one split view controller:

  • item 1: viewcontroller with source nstableview (SourceTableView.class)
  • item 2: viewcontroller with target nstableview (TargetTableView.class)

how can i do this with this project? i know how can i create a split view controller in storyboard. but i dont know, if i have two different classes, how the iBoutlet SourceTabelView of class SourceTableView.class assign the iBoutlet TargetTableView of class TargetTableView.class

UPDATE

var person = [Person]()

NSManagedObject.class

import Foundation
import CoreData

@objc(Person)
public class Person: NSManagedObject {
    @NSManaged public var firstName: String
    @NSManaged public var secondName: String
}

1条回答
甜甜的少女心
2楼-- · 2019-09-15 10:40

Example of drag and drop between two table views inside a split view. Dragging inside one table view and multiple selection will work. Hold the Option key to drag a copy.

The datasource of each table view is the view controller inside the split view. Each table view has its own view controller and each view controller controls one table view. Both view controllers are the same NSViewController subclass:

class ViewController: NSViewController, NSTableViewDelegate, NSTableViewDataSource {

    @IBOutlet weak var myTableView: NSTableView!
    var dataArray: NSMutableArray = ["John Doe", "Jane Doe", "Mary Jane"]

    override func viewDidLoad() {
        super.viewDidLoad()
        myTableView.register(forDraggedTypes: ["com.yoursite.yourproject.yourstringstype"])
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }

    // NSTableViewDataSource data methods

    func numberOfRows(in tableView: NSTableView) -> Int {
        return dataArray.count
    }

    func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
        return dataArray[row] as AnyObject!;
    }

    // NSTableViewDataSource drag methods

    func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
        // the dragging destination needs the strings of the rows to add to its own data,
        // we, the dragging source, need the indexes of the rows to remove the dropped rows.
        pboard.declareTypes(["com.yoursite.yourproject.yourstringstype", "com.yoursite.yourproject.yourindexestype"],
            owner: nil)
        pboard.setData(NSKeyedArchiver.archivedData(withRootObject: (dataArray as NSArray).objects(at:rowIndexes as IndexSet)), forType: "com.yoursite.yourproject.yourstringstype")
        pboard.setData(NSKeyedArchiver.archivedData(withRootObject: rowIndexes), forType: "com.yoursite.yourproject.yourindexestype")
        return true
    }

    func tableView(_ tableView: NSTableView, draggingSession session: NSDraggingSession,
            endedAt screenPoint: NSPoint, operation: NSDragOperation) {
        // remove the dragged rows if the rows are dragged to the trash or were moved to somewhere else.
        var removeRows = false
        if operation == .delete {
            // trash
            removeRows = true
        } else if operation == .move {
            // check if the point where the rows were dropped is inside our table view.
            let windowRect = tableView.convert(tableView.bounds, to: nil)
            let screenRect = view.window!.convertToScreen(windowRect)
            if !NSPointInRect(screenPoint, screenRect) {
                removeRows = true
            }
        }
        if removeRows {
            // remove the rows, the indexes are on the pasteboard
            let data = session.draggingPasteboard.data(forType: "com.yoursite.yourproject.yourindexestype")!
            let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: data) as! NSIndexSet
            (dataArray as NSMutableArray).removeObjects(at: rowIndexes as IndexSet)
            tableView.reloadData()
        }
    }

    // NSTableViewDataSource drop methods

    func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, 
            proposedDropOperation dropOperation: NSTableViewDropOperation) -> NSDragOperation {
        // only accept drop above rows, not on rows.
        if dropOperation == .above {
            // return move if the dragging source allows move
            if info.draggingSourceOperationMask().contains(.move) {
                return .move
            }
            // return copy if the dragging source allows copy
            if info.draggingSourceOperationMask().contains(.copy) {
                return .copy
            }
        }
        return []
    }

    func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int,
            dropOperation: NSTableViewDropOperation) -> Bool {
        // if the rows were moved inside the same table view we do a reorder
        var dropRow = row
        if info.draggingSource() as AnyObject === myTableView as AnyObject &&
            info.draggingSourceOperationMask().contains(.move) {
            // remove the rows from their old position
            let data = info.draggingPasteboard().data(forType: "com.yoursite.yourproject.yourindexestype")!
            let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: data) as! NSIndexSet
            (dataArray as NSMutableArray).removeObjects(at: rowIndexes as IndexSet)
            // recalculate the row of the drop
            dropRow -= rowIndexes.countOfIndexes(in: NSMakeRange(0, dropRow))
        }
        // insert the dragged rows
        let data = info.draggingPasteboard().data(forType: "com.yoursite.yourproject.yourstringstype")!
        let draggedStrings = NSKeyedUnarchiver.unarchiveObject(with: data) as! [Any]
        dataArray.insert(draggedStrings, at:IndexSet(integersIn:dropRow..<(dropRow + draggedStrings.count)))
        tableView.reloadData()
        return true
    }

}

To make dragging to the trash work, subclass NSTableView and override:

override func draggingSession(_ session: NSDraggingSession, sourceOperationMaskFor
        context: NSDraggingContext) -> NSDragOperation {
    let test = super.draggingSession(session, sourceOperationMaskFor: context)
    Swift.print("sourceOperationMaskFor \(test)")
    switch context {
        case .withinApplication:
            return [.move, .copy]
        case .outsideApplication:
            return [.delete]
    }
}

p.s. I'm not familiar with Swift and had some trouble with arrays and indexsets so I used NSMutableArray and NSIndexSet.

查看更多
登录 后发表回答