Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a drag-and-drop zone in Swift

Tags:

macos

swift

cocoa

I recently started using Swift to build OS X apps and I am wondering how I could implement a drag-and-drop zone.

More specifically, I built an app that processes images but, for the moment, the user has to manually enter the path to the input images or use a File Chooser (which is quite annoying). I would like to improve my app allowing the user to input the images by a simple drag-and-drop (I only need to retrieve a String representing the path to the images).

How can I do this?

like image 756
asonnino Avatar asked Mar 24 '15 12:03

asonnino


People also ask

Can you drag and drop on Iphone?

Using drag and drop, people can move or duplicate selected photos, text, and other content by dragging the selection from one location to another. iOS, iPadOS, and macOS support drag and drop through gestures on the touchscreen, interactions with a pointing device, and through full keyboard-access mode.


1 Answers

Here's an example I'm using in an application.

  1. Add conformance to NSDraggingDestination to your subclass declaration if necessary (not needed for NSImageView because it already conforms to the protocol)
  2. Declare an array of accepted types (at least NSFilenamesPboardType)
  3. Register these types with registerForDraggedTypes
  4. Override draggingEntered, draggingUpdated and performDragOperation
  5. Return an NSDragOperation from these methods
  6. Get the file(s) path(s) from the draggingPasteboard array

In my example I've added a function to check if the file extension is amongst the ones we want.

Swift 2

class MyImageView: NSImageView {

    override func drawRect(dirtyRect: NSRect) {
        super.drawRect(dirtyRect)
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        // Declare and register an array of accepted types
        registerForDraggedTypes([NSFilenamesPboardType, NSURLPboardType, NSPasteboardTypeTIFF])
    }

    let fileTypes = ["jpg", "jpeg", "bmp", "png", "gif"]
    var fileTypeIsOk = false
    var droppedFilePath: String?

    override func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
        if checkExtension(sender) {
           fileTypeIsOk = true
           return .Copy
        } else {
           fileTypeIsOk = false
           return .None
        }
    }

    override func draggingUpdated(sender: NSDraggingInfo) -> NSDragOperation {
        if fileTypeIsOk {
            return .Copy
        } else {
            return .None
        }
    }

    override func performDragOperation(sender: NSDraggingInfo) -> Bool {
        if let board = sender.draggingPasteboard().propertyListForType("NSFilenamesPboardType") as? NSArray,
            imagePath = board[0] as? String {
            // THIS IS WERE YOU GET THE PATH FOR THE DROPPED FILE
            droppedFilePath = imagePath
            return true
        }
        return false
    }

    func checkExtension(drag: NSDraggingInfo) -> Bool {
        if let board = drag.draggingPasteboard().propertyListForType("NSFilenamesPboardType") as? NSArray,
            path = board[0] as? String {
            let url = NSURL(fileURLWithPath: path)
            if let fileExtension = url.pathExtension?.lowercaseString {
                return fileTypes.contains(fileExtension)
            }
        }
        return false
    }
}

Swift 3

class MyImageView: NSImageView {

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        // Declare and register an array of accepted types
        register(forDraggedTypes: [NSFilenamesPboardType, NSURLPboardType, NSPasteboardTypeTIFF])
    }

    let fileTypes = ["jpg", "jpeg", "bmp", "png", "gif"]
    var fileTypeIsOk = false
    var droppedFilePath: String?

    override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
        if checkExtension(drag: sender) {
            fileTypeIsOk = true
            return .copy
        } else {
            fileTypeIsOk = false
            return []
        }
    }

    override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation {
        if fileTypeIsOk {
            return .copy
        } else {
            return []
        }
    }

    override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
        if let board = sender.draggingPasteboard().propertyList(forType: "NSFilenamesPboardType") as? NSArray,
            imagePath = board[0] as? String {
            // THIS IS WERE YOU GET THE PATH FOR THE DROPPED FILE
            droppedFilePath = imagePath
            return true
        }
        return false
    }

    func checkExtension(drag: NSDraggingInfo) -> Bool {
        if let board = drag.draggingPasteboard().propertyList(forType: "NSFilenamesPboardType") as? NSArray,
            path = board[0] as? String {
            let url = NSURL(fileURLWithPath: path)
            if let fileExtension = url.pathExtension?.lowercased() {
                return fileTypes.contains(fileExtension)
            }
        }
        return false
    }
}
like image 192
Eric Aya Avatar answered Oct 04 '22 23:10

Eric Aya