Drag & Drop with NSTableViewDiffableDataSource

I'm now trying to add Drag & Drop feature to my NSTableView which uses NSTableViewDiffableDataSource for its data source. However, I can't implement the methods for Drag & Drop like tableView(:, acceptDrop:, row:, dropOperation:).

My code looks like this:

Code Block swift
class MyViewController: NSViewController {
private let tableView: NSTableView = {...}()
private lazy var dataSource = MyDataSource(tableView: tableView)
...
}
class MyDataSource: NSTableViewDiffableDataSource<Section, Model> {
...
    override func tableView(_ tableView: NSTableView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forRowIndexes rowIndexes: IndexSet) {
...
    }
}

Since NSTableViewDiffableDataSource implements NSTableViewDataSource protocol which provides Drag & Drop methods, I thought the above overriding method would work but I got this error.

Method does not override any method from its superclass

What happened? And if this is expected, how can I implement Drag & Drop with NSTableViewDiffableDataSource?

You'll need to annotate any methods from NSTableViewDataSource that you implement with @objc.

For what it's worth, I don't believe any of the drag and drop protocol methods are implemented in the base NSTableViewDiffableDataSource type.

It turns out that you need to subclass NSTableViewDiffableDataSource and implement the drag and drop API in that subclass. It's kinda weird, but that is what needs to be done.

In my case, what I did was this:

  1. implement all my drag and drop API that I usually would in my MyViewController

  2. subclass NSTableViewDiffableDataSource to MyDiffableDataSource

  3. added delegate APIs to MyDiffableDataSource that would ask the delegate for the drag and drop code. For example, it would do something like this:

  • in the MyDiffableDataSource.h file I would have this:

@class MyDiffableDataSource;


@protocol MyDiffableDataSourceDelegate <NSObject>

- (nullable id <NSPasteboardWriting>)tableView:(NSTableView *)tableView
                        pasteboardWriterForRow:(NSInteger)row;

- (void)tableView: (NSTableView *)tableView
  draggingSession: (NSDraggingSession *)session
 willBeginAtPoint: (NSPoint)screenPoint
    forRowIndexes: (NSIndexSet *)rowIndexes;


- (NSDragOperation)tableView: (NSTableView *)tableView
                validateDrop: (id <NSDraggingInfo>)draggingInfo
                 proposedRow: (NSInteger)row
       proposedDropOperation: (NSTableViewDropOperation)dropOperation;



- (BOOL)tableView: (NSTableView *)tableView
       acceptDrop: (id <NSDraggingInfo>)draggingInfo
              row: (NSInteger)row
    dropOperation: (NSTableViewDropOperation)dropOperation;



- (void)tableView: (NSTableView *)tableView
  draggingSession: (NSDraggingSession *)session
     endedAtPoint: (NSPoint)screenPoint
        operation: (NSDragOperation)operation;

@end



@interface MyDiffableDataSource : NSTableViewDiffableDataSource

@property (weak, nonatomic, readwrite) id <MyDiffableDataSourceDelegate> delegate;

@end

In the MyDiffableDataSource.m file, it would do something like this:

- (nullable id <NSPasteboardWriting>)tableView:(NSTableView *)tableView
                        pasteboardWriterForRow:(NSInteger)row

{

    if ([self.delegate respondsToSelector: @selector(tableView:pasteboardWriterForRow:)]){

        return [self.delegate tableView: tableView
                 pasteboardWriterForRow: row];

    }
    return nil;
}

. . . etc

I did not want to have drag and drop code in different classes, which I why I implemented step 2 and 3 using delegate messaging. Of course, I would need MyViewController to be set as the delegate of MyDiffableDataSource.

Too bad there is no real documentation that easily goes over this. You have to guess it when reading that NSTableViewDIffableDataSource conforms to NSTableViewDataSource.

  • EH

Thanks a lot! I had the same issue, and probably would have never figured out the solution on my own.

Drag &amp; Drop with NSTableViewDiffableDataSource
 
 
Q