Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nstableview drag and drop with custom cell views

I work with swift 3 for osx and I'm searching for a drag and drop solution between two NSTableViews in different view controllers.

I have an simple working solution for the case, that each tableview has only one column, no custom cell view and string values.

SourceTableView

import Cocoa

class SourceTableView: NSViewController, NSTableViewDelegate, NSTableViewDataSource {

    @IBOutlet weak var leftTableView: NSTableView!
    var dataArray: NSMutableArray = ["Item 1","Item 2","Item 3"]
    let pbStringType = "NSPasteBoardStringType"
    let pbIndexType = "NSPasteBoardIndexType"


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

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


    func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
        let data = (dataArray as NSArray).objects(at:rowIndexes as IndexSet)
        pboard.declareTypes([pbStringType, pbIndexType], owner: nil)
        pboard.setData(NSKeyedArchiver.archivedData(withRootObject: data), forType: pbStringType)
        pboard.setData(NSKeyedArchiver.archivedData(withRootObject: rowIndexes), forType: pbIndexType)
        return true
    }
}

TargetTableView

import Cocoa

class TargetVC2: NSViewController, NSTableViewDelegate, NSTableViewDataSource {

    @IBOutlet weak var rightTableView: NSTableView!
    var dataArray: NSMutableArray = ["Item 5", "Item 6", "Item 7"]
    let pbStringType = "NSPasteBoardStringType"
    let pbIndexType = "NSPasteBoardIndexType"


    override func viewDidLoad() {
        super.viewDidLoad()
        rightTableView.register(forDraggedTypes: [pbStringType])
    }


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

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



    func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
        let data = dataArray.objects(at:rowIndexes as IndexSet)
        pboard.declareTypes([pbStringType, pbIndexType], owner: nil)
        pboard.setData(NSKeyedArchiver.archivedData(withRootObject: data), forType: pbStringType)
        pboard.setData(NSKeyedArchiver.archivedData(withRootObject: rowIndexes), forType: pbIndexType)
        return true
    }




    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 dropRow = row
        if info.draggingSource() as! NSTableView == rightTableView && tableView == rightTableView && dropOperation == .above  {
            let data = info.draggingPasteboard().data(forType: pbIndexType)!
            let rowIndexes = NSKeyedUnarchiver.unarchiveObject(with: data) as! NSIndexSet
            dataArray.removeObjects(at: rowIndexes as IndexSet)
            dropRow -= rowIndexes.countOfIndexes(in: NSMakeRange(0, dropRow))
        }

        let data = info.draggingPasteboard().data(forType: pbStringType)!
        let draggedStrings = NSKeyedUnarchiver.unarchiveObject(with: data) as! [Any]
        dataArray.insert(draggedStrings, at:IndexSet(integersIn:dropRow..<(dropRow + draggedStrings.count)))
        rightTableView.reloadData()
        return true
    }

}

But now I need a solution for the following case:

  • SourceTableView > one column > custom cell view > values per cell: firstName, secondName
  • TargetTableView > three columns > custom cell view > values per cell: firstName, secondName, Age

the values I get via core data:

func requestValues() {
   var values= [Person]()
   let appdelegate = NSApplication.shared().delegate as! AppDelegate
   let context = appdelegate.persistentContainer.viewContext
   let request = NSFetchRequest<Person>(entityName: "Person")

   do {
      values = try context.fetch(request)
      SourceTableView.reloadData()
   } catch { }
}

But my solution above is not working with my new "wish scenario"

UPDATE For example: I modified my SourceTabelView like this:

import Cocoa

struct structData {
    var firstname:String
    var secondname:String
}

class SourceVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {

    @IBOutlet weak var leftTableView: NSTableView!
    var people = [structData]()
    let pbStringType = "NSPasteBoardStringType"
    let pbIndexType = "NSPasteBoardIndexType"

    override func viewDidLoad() {
        people.append(structData(firstname:"Max",secondname:"Mustermann"))
    }


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

    func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
        return people[row].firstname
    }


    func tableView(_ tableView: NSTableView, writeRowsWith rowIndexes: IndexSet, to pboard: NSPasteboard) -> Bool {
        let data = (people as NSArray).objects(at:rowIndexes as IndexSet)
        pboard.declareTypes([pbStringType, pbIndexType], owner: nil)
        pboard.setData(NSKeyedArchiver.archivedData(withRootObject: data), forType: pbStringType)
        pboard.setData(NSKeyedArchiver.archivedData(withRootObject: rowIndexes), forType: pbIndexType)
        return true
    }
}

It works fine, but if I drag the row with the value "Max" => my app crashes with this error:

2017-06-12 07:51:09.096744+0200 TableView-DragDrop[10315:1489730] -[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x6080000910d0
2017-06-12 07:51:09.100198+0200 TableView-DragDrop[10315:1489730] [General] -[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x6080000910d0
2017-06-12 07:51:09.129238+0200 TableView-DragDrop[10315:1489730] [General] (
    0   CoreFoundation                      0x00007fffaf4d657b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x00007fffc489a1da objc_exception_throw + 48
    2   CoreFoundation                      0x00007fffaf556f14 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
    3   CoreFoundation                      0x00007fffaf449c93 ___forwarding___ + 1059
    4   CoreFoundation                      0x00007fffaf4497e8 _CF_forwarding_prep_0 + 120
    5   Foundation                          0x00007fffb0ed695a _encodeObject + 1241
    6   Foundation                          0x00007fffb0ed7f0c -[NSKeyedArchiver _encodeArrayOfObjects:forKey:] + 460
    7   Foundation                          0x00007fffb0ed695a _encodeObject + 1241
    8   Foundation                          0x00007fffb0f12492 +[NSKeyedArchiver archivedDataWithRootObject:] + 156
    9   TableView-DragDrop                  0x00000001000029a3 _TFC18TableView_DragDrop8SourceVC9tableViewfTCSo11NSTableView13writeRowsWithV10Foundation8IndexSet2toCSo12NSPasteboard_Sb + 915
    10  TableView-DragDrop                  0x0000000100002e5c _TToFC18TableView_DragDrop8SourceVC9tableViewfTCSo11NSTableView13writeRowsWithV10Foundation8IndexSet2toCSo12NSPasteboard_Sb + 108
    11  AppKit                              0x00007fffad6fc109 -[NSTableView _sendDataSourceWriteDragDataWithIndexes:toPasteboard:] + 102
    12  AppKit                              0x00007fffad6fcd06 -[NSTableView _performClassicDragOfIndexes:hitRow:event:] + 180
    13  AppKit                              0x00007fffad21e7b5 -[NSTableView _performDragFromMouseDown:] + 468
    14  AppKit                              0x00007fffad21cadf -[NSTableView mouseDown:] + 735
    15  AppKit                              0x00007fffad84024f -[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 6341
    16  AppKit                              0x00007fffad83ca6c -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 1942
    17  AppKit                              0x00007fffad83bf0a -[NSWindow(NSEventRouting) sendEvent:] + 541
    18  AppKit                              0x00007fffad6c0681 -[NSApplication(NSEvent) sendEvent:] + 1145
    19  AppKit                              0x00007fffacf3b427 -[NSApplication run] + 1002
    20  AppKit                              0x00007fffacf05e0e NSApplicationMain + 1237
    21  TableView-DragDrop                  0x000000010000444d main + 13
    22  libdyld.dylib                       0x00007fffc517b235 start + 1
    23  ???                                 0x0000000000000003 0x0 + 3
)
2017-06-12 07:51:09.148230+0200 TableView-DragDrop[10315:1489730] *** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
like image 977
Ghost108 Avatar asked Oct 17 '22 11:10

Ghost108


1 Answers

The reason for crash looks like, the Swift runtime is not able to find encodeWithCoder for your struct type.

You may need to implement encodeWithCoder. Your structData struct should conform to encodeWithCoder.

Please have a look at following links on how to do the same

Example-1 Example-2 Example-3

like image 137
Karthik Kumar Avatar answered Oct 20 '22 23:10

Karthik Kumar