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.
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
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
}
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
}
}
Related
I'm apparently designing a drag and drop dropbox which can either select files by clicking it or dragging and dropping the files on it, and I want the selected files to be visible in a table next to it. My design logic is that whenever the user selects files from an NSOpenPanel, it passes the selected file paths into the CoreData and then an array retrieves them one by one from the CoreData, and finally, update the NSTableView's content by using reloadData().
Basically, my problem is that whenever I try to call ViewController().getDroppedFiles() from DropboxButton class, I always get a Fatal error: unexpectedly found nil while unwrapping an optional value.
My ViewController.swift:
import Cocoa
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
getDroppedFiles()
}
#IBOutlet weak var DroppedFilesTableView: NSTableView!
var droppedFiles: [DroppedFiles] = [] // Core Data class definition: DroppedFiles
func numberOfRows(in tableView: NSTableView) -> Int {
return droppedFiles.count
}
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let droppedFilesCollection = droppedFiles[row]
if (tableView?.identifier)!.rawValue == "fileNameColumn" {
if let fileNameCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "fileNameCell")) as? NSTableCellView {
fileNameCell.textField?.stringValue = droppedFilesCollection.fileName!
return fileNameCell
}
} else if (tableView?.identifier)!.rawValue == "filePathColumn" {
if let filePathCell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "filePathCell")) as? NSTableCellView {
filePathCell.textField?.stringValue = droppedFilesCollection.filePath!
return filePathCell
}
}
return nil
}
#IBAction func DropboxClicked(_ sender: NSButton) {
// selected file paths
for filePath in selectedFilePaths {
if let context = (NSApp.delegate as? AppDelegate)?.persistentContainer.viewContext {
let droppedFilesData = DroppedFiles(context: context)
droppedFilesData.fileName = getFileName(withPath: filePath)
droppedFilesData.filePath = filePath
do {
try context.save()
} catch {
print("Unable to save core data.")
}
}
getDroppedFiles()
}
}
func getDroppedFiles() {
if let context = (NSApp.delegate as? AppDelegate)?.persistentContainer.viewContext {
do {
try droppedFiles = context.fetch(DroppedFiles.fetchRequest())
} catch {
print("Unable to fetch core data.")
}
}
DroppedFilesTableView.reloadData() // Fatal Error: unexpectedly found nil while unwrapping an optional value (whenever I call this function in other class)
}
}
I'm using a push button (NSButton) as the dropbox (it has its own class), which can easily be clicked and also supports dragging options.
My DropboxButton.swift:
import Cocoa
class DropboxButton: NSButton {
required init?(coder: NSCoder) {
super.init(coder: coder)
registerForDraggedTypes([NSPasteboard.PasteboardType.URL, NSPasteboard.PasteboardType.fileURL])
}
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
// some other codes
return .copy
}
override func draggingExited(_ sender: NSDraggingInfo?) {
// some other codes
}
override func draggingEnded(_ sender: NSDraggingInfo) {
// some other codes
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
guard let pasteboard = sender.draggingPasteboard.propertyList(forType: NSPasteboard.PasteboardType(rawValue: "NSFilenamesPboardType")) as? NSArray,
let filePaths = pasteboard as? [String] else {
return false
}
for filePath in filePaths {
if let context = (NSApp.delegate as? AppDelegate)?.persistentContainer.viewContext {
let droppedFilesData = DroppedFiles(context: context)
droppedFilesData.fileName = getFileName(withPath: filePath)
droppedFilesData.filePath = filePath
do {
try context.save()
} catch {
print("Unable to save core data.")
}
}
ViewController().getDroppedFiles() // found nil with reloadData() in ViewController.swift
}
return true
}
}
And this is my interface and code logic:
So, how can I reloadData() for the table view in my ViewController class from another class (DropboxButton: NSButton) so that whenever the user drags and drops files into the dropbox, the table view will reload?
P.S. To get this done means a lot to me, I really need to get this fixed in a short time, is there anyone can spend some time and help me?
You need to call getDroppedFiles() on a loaded instance of ViewController.
With ViewController().getDroppedFiles() you're creating a new instance of ViewController that is not shown anywhere (so controls are not initialized resulting in the nil error).
I found this solution useful for my case.
I used observer to pass through data and call functions from other controller classes, now I understand that I was creating a new instance of ViewController which is not loaded. Here is my code:
ViewController.swift:
class ViewController: NSViewController {
// other codes
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(getDroppedFiles), name: NSNotification.Name(rawValue: "reloadTableViewData"), object: nil)
}
#objc func getDroppedFiles() {
DroppedFilesTableView.reloadData()
}
}
DropboxButton.swift:
class DropboxButton: NSButton {
// other codes
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
// other codes
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTableViewData"), object: nil)
return true
}
}
And now, everything works perfectly, I can even add an userInfo: to pass data between files and classes.
This is more or less my Main.storyboard situation:
where I have a root UITabBarController with 5 possibile choices. Then, I want that some UIViewControllers can rotate to landscape while I want also some other UIViewControllers to have only landscape mode. So I have written this file.swift:
class CustomNavigationController: UINavigationController, UINavigationControllerDelegate {
override func shouldAutorotate() -> Bool {
if (self.topViewController?.isKindOfClass(HomeViewController) != nil) {return false}
else if (self.topViewController?.isKindOfClass(ServicesViewController) != nil) {return false}
else if (self.topViewController?.isKindOfClass(PhotoGalleryViewController) != nil) {return false}
else if (self.topViewController?.isKindOfClass(SelectMapViewController) != nil) {return false}
else if (self.topViewController?.isKindOfClass(MapViewController) != nil) {return false}
else {return true}
}
}
class CustomTabBarController: UITabBarController, UITabBarControllerDelegate {
override func shouldAutorotate() -> Bool {
return (self.selectedViewController as! UINavigationController).shouldAutorotate()
}
}
and I have assigned to all UINavigationControllers the same class
CustomNavigationController while I have assigned CustomTabBarController class to UITabBarController.
The result is that no view controller do not rotates. Is this because I have assigned the same class to them? Shall I create a custom navigation controller class for each UINavigationController I have?
UPDATE
A partial solution I found, even if it's a little intricate, is the following. I have modified the previous file like that:
class CustomNavigationController: UINavigationController, UINavigationControllerDelegate {
override func shouldAutorotate() -> Bool {
return (self.topViewController?.shouldAutorotate())!
}
}
class CustomTabBarController: UITabBarController, UITabBarControllerDelegate {
override func shouldAutorotate() -> Bool {
return (self.selectedViewController as! UINavigationController).shouldAutorotate()
}
}
Then, in view controllers where rotation is allowed I simply have:
override func shouldAutorotate() -> Bool {
return true
}
while in view controllers where rotation is not allowed I have:
override func shouldAutorotate() -> Bool {
return false
}
override func viewWillAppear(animated: Bool) {
let value = UIInterfaceOrientation.Portrait.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
Anyway there's a little problem because the animation which sets mode to portrait is not correct meaning that the width of the screen is not adjusted. if I go from a landscape view controller to a portrait only view controller then the view controller frame is not correct. I get
instead of this:
Try this in AppDelegate
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if let rootController = self.window?.rootViewController as? UITabBarController {
if let navigationController = rootController.selectedViewController as? UINavigationController {
let controller = navigationController.topViewController!
if controller.isKindOfClass(HomeViewController.classForCoder()) {
return UIInterfaceOrientationMask.Portrait
}
if controller.isKindOfClass(ServicesViewController.classForCoder()) {
return UIInterfaceOrientationMask.Portrait
}
if controller.isKindOfClass(PhotoGalleryViewController.classForCoder()) {
return UIInterfaceOrientationMask.Portrait
}
if controller.isKindOfClass(SelectMapViewController.classForCoder()) {
return UIInterfaceOrientationMask.Portrait
}
if controller.isKindOfClass(MapViewController.classForCoder()) {
return UIInterfaceOrientationMask.Portrait
}
}
}
return UIInterfaceOrientationMask.All
}
Update:
This method forces application to change orientation in ViewController that should be only in Portrait (HomeViewController, ServicesViewController, PhotoGalleryViewController, SelectMapViewController, MapViewController):
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let value = UIInterfaceOrientation.Portrait.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
how to implement a drag-and-drop zone in swift 2.0?
I built an app that processes kext files but, for now, i have to manually enter the path to the input kext.
my question is: how to get file path by performing a drag and drop on a zone?
[Update to Swift 4.0 and Xcode 9]
Inspired by Implementing a drag-and-drop zone in Swift
Add a NSView to your main view and subclass.
This code works perfect in Swift 4.0 and macOS 10.13 High Sierra!
import Cocoa
class DropView: NSView {
var filePath: String?
let expectedExt = ["kext"] //file extensions allowed for Drag&Drop (example: "jpg","png","docx", etc..)
required init?(coder: NSCoder) {
super.init(coder: coder)
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.gray.cgColor
registerForDraggedTypes([NSPasteboard.PasteboardType.URL, NSPasteboard.PasteboardType.fileURL])
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
if checkExtension(sender) == true {
self.layer?.backgroundColor = NSColor.blue.cgColor
return .copy
} else {
return NSDragOperation()
}
}
fileprivate func checkExtension(_ drag: NSDraggingInfo) -> Bool {
guard let board = drag.draggingPasteboard().propertyList(forType: NSPasteboard.PasteboardType(rawValue: "NSFilenamesPboardType")) as? NSArray,
let path = board[0] as? String
else { return false }
let suffix = URL(fileURLWithPath: path).pathExtension
for ext in self.expectedExt {
if ext.lowercased() == suffix {
return true
}
}
return false
}
override func draggingExited(_ sender: NSDraggingInfo?) {
self.layer?.backgroundColor = NSColor.gray.cgColor
}
override func draggingEnded(_ sender: NSDraggingInfo) {
self.layer?.backgroundColor = NSColor.gray.cgColor
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
guard let pasteboard = sender.draggingPasteboard().propertyList(forType: NSPasteboard.PasteboardType(rawValue: "NSFilenamesPboardType")) as? NSArray,
let path = pasteboard[0] as? String
else { return false }
//GET YOUR FILE PATH !!!
self.filePath = path
Swift.print("FilePath: \(path)")
return true
}
}
To use this code you have to set "macOS Deployment Target" to 10.13
This implementation allows for multiple files.
Just set the view class to DragView in Interface Builder, implement DragViewDelegate in your controller, then hook up the delegate outlet in Interface Builder. This way you get a delegate callback with the file URLs.
func dragViewDidReceive(fileURLs: [URL])
{
// Yay!
}
DragView.swift
//
// DragView.swift
//
// Copyright (c) 2020 Geri Borbás http://www.twitter.com/_eppz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import Cocoa
#objc protocol DragViewDelegate
{
func dragViewDidReceive(fileURLs: [URL])
}
class DragView: NSView
{
#IBOutlet weak var delegate: DragViewDelegate?
let fileExtensions = ["kext"]
required init?(coder: NSCoder)
{
super.init(coder: coder)
color(to: .clear)
registerForDraggedTypes([.fileURL])
}
override func draggingEntered(_ draggingInfo: NSDraggingInfo) -> NSDragOperation
{
var containsMatchingFiles = false
draggingInfo.draggingPasteboard.readObjects(forClasses: [NSURL.self], options: nil)?.forEach
{
eachObject in
if let eachURL = eachObject as? URL
{
containsMatchingFiles = containsMatchingFiles || fileExtensions.contains(eachURL.pathExtension.lowercased())
if containsMatchingFiles { print(eachURL.path) }
}
}
switch (containsMatchingFiles)
{
case true:
color(to: .secondaryLabelColor)
return .copy
case false:
color(to: .disabledControlTextColor)
return .init()
}
}
override func performDragOperation(_ draggingInfo: NSDraggingInfo) -> Bool
{
// Collect URLs.
var matchingFileURLs: [URL] = []
draggingInfo.draggingPasteboard.readObjects(forClasses: [NSURL.self], options: nil)?.forEach
{
eachObject in
if
let eachURL = eachObject as? URL,
fileExtensions.contains(eachURL.pathExtension.lowercased())
{ matchingFileURLs.append(eachURL) }
}
// Only if any,
guard matchingFileURLs.count > 0
else { return false }
// Pass to delegate.
delegate?.dragViewDidReceive(fileURLs: matchingFileURLs)
return true
}
override func draggingExited(_ sender: NSDraggingInfo?)
{ color(to: .clear) }
override func draggingEnded(_ sender: NSDraggingInfo)
{ color(to: .clear) }
}
extension DragView
{
func color(to color: NSColor)
{
self.wantsLayer = true
self.layer?.backgroundColor = color.cgColor
}
}
I'm working on an OS X App and, I can't seem to get Drag and Drop to work. I've Googled a lot, but most posts about this subject are at least a few years old and none of them tells me the missing link I have in my thoughts.
Anyway, here is what I'm trying to do. I have an image somewhere on my desktop and I want the ability to drag and drop that into my Custom NSView. The custom view is a child object of a custom NSView named CircularImageView and is layer backed and only shows a circular shaped image on the screen.
Here's the code:
import Cocoa
import MCTools
#objc public protocol DragAndDropCircularImageViewDelegate {
func imageDumped(sender: AnyObject!)
}
#IBDesignable #objc public class DragAndDropCircularImageView: CircularImageView {
// This class provides the Drag And Drop Feature to the CircularImageView Class.
// MARK: New in this class
var highlight: Bool = false
public var delegate: DragAndDropCircularImageViewDelegate?
private func registerForDraggedImages() {
self.registerForDraggedTypes(NSImage.imageTypes())
}
// MARK: CircularImageView Stuff
public override var image: NSImage? {
didSet {
if let newImage = image {
delegate?.imageDumped(self)
}
}
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
self.registerForDraggedImages()
}
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.registerForDraggedImages()
}
public override func updateLayer() {
super.updateLayer()
if highlight == true {
}
}
// MARK: NS Dragging Destination Protocol
public override func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
// When a drag enters our drop zone.
if NSImage.canInitWithPasteboard(sender.draggingPasteboard()) {
if ((sender.draggingSourceOperationMask().rawValue & NSDragOperation.Copy.rawValue) > 0) {
highlight = true
self.needsLayout = true
sender.enumerateDraggingItemsWithOptions(.Concurrent, forView: self, classes: [NSPasteboardItem.self], searchOptions: [NSPasteboardURLReadingContentsConformToTypesKey: self], usingBlock: { (draggingItem, idx, stop) -> Void in
return
})
}
return NSDragOperation.Copy
}
return NSDragOperation.None
}
public override func draggingExited(sender: NSDraggingInfo?) {
// When drag exits our drop zone remove highlight of the drop zone.
println("\(self)draggingExited")
highlight = false
self.needsLayout = true
}
public override func prepareForDragOperation(sender: NSDraggingInfo) -> Bool {
// Update view for hovering drop.
println("\(self)prepareForDragOperation")
highlight = false
self.needsLayout = true
// Can we accept the drop?
return NSImage.canInitWithPasteboard(sender.draggingPasteboard())
}
public override func performDragOperation(sender: NSDraggingInfo) -> Bool {
// Handle the drop data.
println("\(self)performDragOperation \(sender)")
if NSImage.canInitWithPasteboard(sender.draggingPasteboard()) {
self.image = NSImage(pasteboard: sender.draggingPasteboard())
}
return true
}
// MARK: Interface Builder Stuff
}
I have seen some posts that I should be using:
self.registerForDraggedTypes([NSFilenamesPboardType])
instead of:
self.registerForDraggedTypes(NSImage.imageTypes())
But this doesn't seem to work in my case, when I'm using NSFileNamesPboardType I get the following debug message even before any of the NSDraggingDestination protocol messages have been called:
2015-05-07 11:07:19.583 CircularImageViewTest[44809:14389647] -[CircularView.DragAndDropCircularImageView copyWithZone:]: unrecognized selector sent to instance 0x608000166d80
(lldb) p 0x608000166d80
(Int) $R0 = 106102873550208
I don't understand how this works. Somewhere the frameworks try to copyWithZone on an integer? Can anyone explain this to me?
Any help would be appreciated. Thanks in advance.
Ok, the code below works. It was all caused by sender.enumerateDraggingItemsWithOptions in draggingEntered. Something goes wrong in the Apple frameworks when it is called.
import Cocoa
import MCTools
#objc public protocol DragAndDropCircularImageViewDelegate {
func imageDumped(sender: AnyObject!)
}
#IBDesignable #objc public class DragAndDropCircularImageView: CircularImageView {
// This class provides the Drag And Drop Feature to the CircularImageView Class.
// MARK: New in this class
var highlight: Bool = false
public weak var delegate: DragAndDropCircularImageViewDelegate?
private func registerForDraggedImages() {
// self.registerForDraggedTypes(NSImage.imageTypes())
self.registerForDraggedTypes([NSFilenamesPboardType])
}
// MARK: CircularImageView Stuff
public override var image: NSImage? {
didSet {
if let newImage = image {
delegate?.imageDumped(self)
}
}
}
public required init?(coder: NSCoder) {
super.init(coder: coder)
self.registerForDraggedImages()
}
public override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
self.registerForDraggedImages()
}
public override func updateLayer() {
super.updateLayer()
if highlight == true {
}
}
// MARK: NS Dragging Destination Protocol
public override func draggingEntered(sender: NSDraggingInfo) -> NSDragOperation {
// When a drag enters our drop zone.
if NSImage.canInitWithPasteboard(sender.draggingPasteboard()) {
if ((sender.draggingSourceOperationMask().rawValue & NSDragOperation.Copy.rawValue) > 0) {
highlight = true
self.needsLayout = true
}
return NSDragOperation.Copy
}
return NSDragOperation.None
}
public override func draggingExited(sender: NSDraggingInfo?) {
// When drag exits our drop zone remove highlight of the drop zone.
println("\(self)draggingExited")
highlight = false
self.needsLayout = true
}
public override func prepareForDragOperation(sender: NSDraggingInfo) -> Bool {
// Update view for hovering drop.
println("\(self)prepareForDragOperation")
highlight = false
self.needsLayout = true
// Can we accept the drop?
return NSImage.canInitWithPasteboard(sender.draggingPasteboard())
}
public override func performDragOperation(sender: NSDraggingInfo) -> Bool {
// Handle the drop data.
println("\(self)performDragOperation \(sender)")
if NSImage.canInitWithPasteboard(sender.draggingPasteboard()) {
self.image = NSImage(pasteboard: sender.draggingPasteboard())
self.delegate!.imageDumped(self)
}
return true
}
// MARK: Interface Builder Stuff
}
I have some code, that works in a command line tool. Now i wanted to make it a Drop-Applet to get by the terminal and paths. I am dropping some files to it and as long as the Debugger is attached it works like a charm.
So far so good, but when I start it directly (from the Xcode output directory), it seems the drag/drop is not accepted. (Animation of the file back to the origin).
class dragView : NSView, NSDraggingDestination {
required init(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame: NSRect) {
super.init(frame: frame)
let types = [NSFilenamesPboardType, NSURLPboardType]
registerForDraggedTypes(types)
}
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
NSColor.whiteColor().set()
NSRectFill(dirtyRect)
}
override func draggingEntered(sender: NSDraggingInfo!) -> NSDragOperation {
return NSDragOperation.Copy
}
override func draggingUpdated(sender: NSDraggingInfo!) -> NSDragOperation {
return NSDragOperation.Copy
}
override func performDragOperation(sender: NSDraggingInfo!) -> Bool {
let pboard: NSPasteboard = sender.draggingPasteboard()
let array : [String] = pboard.propertyListForType(String(NSFilenamesPboardType)) as [String]
for item in array
{
...
What am I missing here?
Your code doesn't work cause you must register dragged type into init(coder:) function.
This code works fine in Swift 3
import Cocoa
class DropView : NSView {
required init?(coder: NSCoder) {
super.init(coder: coder)
let types = [NSFilenamesPboardType, NSURLPboardType]
register(forDraggedTypes: types)
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.white.cgColor
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
return .copy
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
guard let pasteboard = sender.draggingPasteboard().propertyList(forType: "NSFilenamesPboardType") as? NSArray,
let path = pasteboard[0] as? String
else { return false }
//GET YOUR FILE PATH !!
Swift.print("FilePath: \(path)")
return true
}
}
A more usefull example here