8

In Swift, how can I build an area in a window of my Mac app where a user can drag-and-drop a folder onto this area, and have my app receive the path of the folder?

In principle, it seems to me that this is a similar concept to Apple's CocoaDragAndDrop example app. I've tried to work my way through understanding that source code, but the app is written in Objective-C and I've not successfully managed to replicate its functionality in the app I am building.

Thank you in advance.

sebthedev
  • 288
  • 3
  • 10
  • Did you get anywhere with this problem? if you did then it'd be great if you could post your solution for others (Like me!) – Will Richardson Jan 06 '15 at 08:31
  • @JavaNut13 Nope! I was never able to work out a solution. I managed to allow users to drop files/folders onto my app's dock icon by adding the relevant [Document Types](https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/DocumentInteraction_TopicsForIOS/Articles/RegisteringtheFileTypesYourAppSupports.html) to my target in Xcode, and then adding a `func application(sender: NSApplication, openFile theDroppedFilePath: String) { PROCESS YOUR FILES HERE }` to my AppDelegate.swift. Hope this helps! – sebthedev Jan 06 '15 at 17:39
  • If I do work it out I'll post an answer here I guess. – Will Richardson Jan 07 '15 at 08:56
  • Note that int the Cocoa Drag&Drop example, you can comment out the "registerForDraggedTypes" & it still works... because it is a default functionality of NSImageView... Therefore in swift, if you use an NSImageView, it will also works for images at least. I am still looking for a working example (Swift or Obj C) of drag & drop functionality on NSView.. I was never able to get registerForDraggedTypes to work... – user2962499 Mar 13 '15 at 07:57

3 Answers3

6

In a custom NSView:

override init(frame frameRect: NSRect)
{
    super.init(frame: frameRect)
    registerForDraggedTypes([kUTTypeFileURL,kUTTypeImage])
}

override func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation
{
    println("dragging entered")
    return NSDragOperation.Copy
}

The catch is, to get it working I had to put that custom view as a leaf and the last view in the storyboard

user2962499
  • 486
  • 6
  • 10
  • 1
    OMG. THANK YOU for the last annotation! I was driving crazy because I had an Imageview inside the destination view. – rmvz3 May 19 '15 at 00:58
  • Rather than in `init` you should place the register-call in `awakeFromNib`. Else you'll hear Swift croak. – qwerty_so Jul 18 '16 at 11:51
2

This worked for me (on a NSWindow subclass):

In awakeFromNib:

registerForDraggedTypes([NSFilenamesPboardType])

Then add the following operations (at least draggingEntered and performDragOperation) to the window (or view):

func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
    let sourceDragMask = sender.draggingSourceOperationMask()
    let pboard = sender.draggingPasteboard()!
    if pboard.availableTypeFromArray([NSFilenamesPboardType]) == NSFilenamesPboardType {
        if sourceDragMask.rawValue & NSDragOperation.Generic.rawValue != 0 {
            return NSDragOperation.Generic
        }
    }
    return NSDragOperation.None
}

func draggingUpdated(sender: NSDraggingInfo) -> NSDragOperation {
    return NSDragOperation.Generic
}

func prepareForDragOperation(sender: NSDraggingInfo) -> Bool {
    return true
}

func performDragOperation(sender: NSDraggingInfo) -> Bool {
   // ... perform your magic
   // return true/false depending on success
}
Rien
  • 638
  • 6
  • 10
2

my 2 cents for OSX - swift 5 (and fixed for loading from XIBs/storybord.)

//  Created by ing.conti on 31th jan 2020.
//

import Cocoa

class AnalysisView: NSView {

    override init(frame frameRect: NSRect)
    {
        super.init(frame: frameRect)
        self.registerMyTypes()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.registerMyTypes()
    }


    final private func registerMyTypes()
    {
        registerForDraggedTypes(
            [NSPasteboard.PasteboardType.URL,
             NSPasteboard.PasteboardType.fileURL,
             NSPasteboard.PasteboardType.png,
             NSPasteboard.PasteboardType.fileNameType(forPathExtension: "wtf")
        ])
    }



    override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
        print("draggingEntered")
        return NSDragOperation.copy
    }

    override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool {
        let allow = true // check your types...
        print("prepareForDragOperation")
        return allow
    }

    override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {

        let pasteBoard = sender.draggingPasteboard
        print("performDragOperation")

        if let urls = pasteBoard.readObjects(forClasses: [NSURL.self]) as? [URL]{
            // consume them...
            print(urls)
            return true
        }
        return false
    }
}
ingconti
  • 10,876
  • 3
  • 61
  • 48