NSTableView Drag and Drop

NSTableView Drag and Drop in Cocoa, Objective C

Created By: Debasis Das (9-Dec-2014)
In this Post we will create a demo application to demonstrate NSTableView Drag and Drop.

The drag and drop is allowed between a Source Table View and a Target Table View and vice versa.

Drag and drop is also allowed inside the target table view to reorder the data.

The source code can be downloaded from https://github.com/knowstack/DDCocoaSampleCode/tree/master/KSTableViewDragDrop

  • Open Xcode and Create a new cocoa project.
  • In the MainMenu.xib add two cell based table views.
  • Make the AppDelegate as the datasource and delegate for both the tables.
  • Add the below code to your AppDelegate.h and AppDelegate.m files respectively

The Swift Implementation can be found at the following link

http://www.knowstack.com/swift-nstableview-drag-drop-in/

NSTableView_DragDrop

//
//  AppDelegate.h
//  KSTableViewDragDrop
//
//  Created by Debasis Das on 12/9/14.
//  Copyright (c) 2014 Knowstack. All rights reserved.
//

#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate,NSTableViewDelegate, NSTableViewDataSource>
@property (nonatomic, retain) NSMutableArray *sourceDataArray;
@property (nonatomic, retain) NSMutableArray *targetDataArray;
@property (nonatomic, assign) IBOutlet NSTableView *sourceTableView;
@property (nonatomic, assign) IBOutlet NSTableView *targetTableView;
@end

//
//  AppDelegate.m
//  KSTableViewDragDrop
//
//  Created by Debasis Das on 12/9/14.
//  Copyright (c) 2014 Knowstack. All rights reserved.
//

#import "AppDelegate.h"
@interface AppDelegate ()

@property (weak) IBOutlet NSWindow *window;
@end

@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    [self setSourceDataArray:[NSMutableArray array]];
    [self setTargetDataArray:[NSMutableArray array]];
    
    [self.sourceTableView setDraggingSourceOperationMask:NSDragOperationLink forLocal:NO];
    [self.sourceTableView setDraggingSourceOperationMask:NSDragOperationMove forLocal:YES];
    [self.sourceTableView registerForDraggedTypes:[NSArray arrayWithObject:NSStringPboardType]];

    [self.targetTableView setDraggingSourceOperationMask:NSDragOperationLink forLocal:NO];
    [self.targetTableView setDraggingSourceOperationMask:NSDragOperationMove forLocal:YES];
    [self.targetTableView registerForDraggedTypes:[NSArray arrayWithObject:NSStringPboardType]];

    [self createDemoData];
    [self.sourceTableView reloadData];
}

-(void)createDemoData
{
    [self setSourceDataArray:[NSMutableArray arrayWithArray:@[@"John Doe",@"Jane Doe",@"Debasis Das",@"Mary Jane",@"John Issac",@"Bret Smith"]]];
}

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    long recordCount = 0;
    if (tableView == self.sourceTableView)
    {
        recordCount = [self.sourceDataArray count];
    }
    else if (tableView == self.targetTableView)
    {
        recordCount = [self.targetDataArray count];
    }
    return recordCount;
}

- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
    NSString *aString;
    if (aTableView == self.sourceTableView)
    {
        aString = [self.sourceDataArray objectAtIndex:rowIndex];
    }
    else if (aTableView == self.targetTableView)
    {
        aString = [self.targetDataArray objectAtIndex:rowIndex];
    }
    else
    {
        aString = @"This table is not handled";
    }
    return aString;
}

#pragma mark Drag & Drop Delegates
- (BOOL)tableView:(NSTableView *)aTableView
writeRowsWithIndexes:(NSIndexSet *)rowIndexes
     toPasteboard:(NSPasteboard*)pboard
{
    if(aTableView == self.sourceTableView ||
       self.targetTableView)
    {
        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
        [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
        [pboard setData:data forType:NSStringPboardType];
        return YES;
    }
    else
    {
        return NO;
    }
}

- (NSDragOperation)tableView:(NSTableView*)tv
                validateDrop:(id )info
                 proposedRow:(NSInteger)row
       proposedDropOperation:(NSTableViewDropOperation)op
{
    return NSDragOperationEvery;
}

- (BOOL)tableView:(NSTableView*)tv
       acceptDrop:(id )info
              row:(NSInteger)row
    dropOperation:(NSTableViewDropOperation)op
{
    NSData *data = [[info draggingPasteboard] dataForType:NSStringPboardType];
    NSIndexSet *rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    //REORDERING IN THE SAME TABLE VIEW BY DRAG & DROP
    if (([info draggingSource] == self.targetTableView) & (tv == self.targetTableView))
    {
        NSArray *tArr = [self.targetDataArray objectsAtIndexes:rowIndexes];
        [self.targetDataArray removeObjectsAtIndexes:rowIndexes];
        if (row > self.targetDataArray.count)
        {
            [self.targetDataArray insertObject:[tArr objectAtIndex:0] atIndex:row-1];
        }
        else
        {
            [self.targetDataArray insertObject:[tArr objectAtIndex:0] atIndex:row];
        }
        [self.targetTableView reloadData];
        [self.targetTableView deselectAll:nil];
    }

    //DRAG AND DROP ACROSS THE TABLES
    else if (([info draggingSource] == self.sourceTableView) & (tv == self.targetTableView))
    {
        NSArray *tArr = [self.sourceDataArray objectsAtIndexes:rowIndexes];
        [self.sourceDataArray removeObjectsAtIndexes:rowIndexes];
        [self.targetDataArray addObject:[tArr objectAtIndex:0]];
        [self.sourceTableView reloadData];
        [self.sourceTableView deselectAll:nil];
        [self.targetTableView reloadData];
    }
    else if (([info draggingSource] == self.targetTableView) & (tv == self.sourceTableView))
    {
        NSArray *tArr = [self.targetDataArray objectsAtIndexes:rowIndexes];
        [self.targetDataArray removeObjectsAtIndexes:rowIndexes];
        [self.sourceDataArray addObject:[tArr objectAtIndex:0]];
        [self.targetTableView reloadData];
        [self.targetTableView deselectAll:nil];
        [self.sourceTableView reloadData];
    }
    return YES;
}
@end
Posted in Cocoa, Objective C, Swift Tagged with: , , , , ,
8 comments on “NSTableView Drag and Drop
  1. jfluhr says:

    Any plans to implement this in Swift instead of Objective-C? Or maybe reply with what would need to be done differently if implemented in Swift? Thanks!

    • Debasis Das says:

      I will implement this in Swift and will post the code here.

      • jfluhr says:

        Thanks! Looking forward to it.

        • Debasis Das says:

          Hi,

          Please find the swift implementation of NSTableView Drag and Drop

          http://www.knowstack.com/swift-nstableview-drag-drop-in/

          Let me know if you have any questions.

          • Jody Fluhr says:

            Thanks for the Swift implementation! I’m experimenting now.

            I put a println statement in each of the tableView functions. Interesting thing, upon building and executing the app, numberOfRowsInTableView is called twice then the first tableView function is called approximately 28 times. Just wondering why the first tableView function is called so many times without me interacting with the app at all?

          • Debasis Das says:

            the numberOfRowsInTableView is a tableview datasource method which is called automatically.
            Normally if we put a log in a delegate method it will continuously be called to see if there is any change to the datasource.
            Alternatively to force call a datasource method one can call the tableView.reloadData() method.

            A similar behavior can be observed in drawRect method of a NSView Class.
            If you have a custom class which is a subclass of NSView and you have overridden the drawRect method. the method gets called repeatedly when ever the screen resizes or you are clicking an element inside the view.

            Right now you need not worry about the numberOfRowsInTableView or ObjectValueForTableColumn method being called repeatedly.

            Let me know if you any further questions and I will be happy to help.

            Best Regards
            Debasis

  2. Jody Fluhr says:

    Your equivalent swift example has been very helpful. I successfully modified the code to allow the target table contents to be dragged to the source table. I really appreciate your help!

    My next challenge is to utilize the pdf view object with a swift app delegate but I am having difficulty. Do you have any examples? I simply want to display a pdf on my desktop in a pdf view object.

    Any and all help is greatly appreciated!

    • Debasis Das says:

      Following is the code to load a PDF in a pdf viewer

      //
      // AppDelegate.swift
      // KSPDFViewer

      import Cocoa
      import Quartz

      @NSApplicationMain
      class AppDelegate: NSObject, NSApplicationDelegate {

      @IBOutlet weak var window: NSWindow!
      @IBOutlet weak var pdfViewer: PDFView!

      func applicationDidFinishLaunching(aNotification: NSNotification) {
      let fileURL:NSURL = NSBundle.mainBundle().URLForResource(“SampleFile”, withExtension: “pdf”)!
      var pdfDocument:PDFDocument = PDFDocument.self.init(URL: fileURL)
      pdfViewer.setDocument(pdfDocument)
      }

      func applicationWillTerminate(aNotification: NSNotification) {
      // Insert code here to tear down your application
      }

      }

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Recent Posts


Hit Counter provided by technology news