【发布时间】:2019-11-03 08:36:39
【问题描述】:
我需要能够将文件表示(在我的情况下为 pdf)从我的应用程序中包含的 NSView 拖到桌面或其他支持打开 PDF 文件的应用程序上。
【问题讨论】:
标签: swift macos drag-and-drop nsview
我需要能够将文件表示(在我的情况下为 pdf)从我的应用程序中包含的 NSView 拖到桌面或其他支持打开 PDF 文件的应用程序上。
【问题讨论】:
标签: swift macos drag-and-drop nsview
我花了几个小时试图让它在我自己的应用程序中运行,我想我应该在这里添加我的解决方案,因为网上有很多半解决方案,其中一些依赖于 Obj-C 扩展,而另一些依赖于已过时,不再受支持。我希望这篇文章是我希望在我自己的搜索中找到的那种帖子。我也知道系统的所有细节(例如,使用文件协调器而不是直接写入),但这似乎是实现所需的最少代码。
我还提供了一个简单的 Swift NSView 实现。
操作分为三个主要阶段。
您需要通过实现NSPasteboardItemDataProvider 协议使您的视图(或其他控件)成为拖动的“数据提供者”。所需的大部分工作(开始拖动除外)发生在以下协议函数中。
func pasteboard(_ pasteboard: NSPasteboard?, item _: NSPasteboardItem, provideDataForType type: NSPasteboard.PasteboardType)
此部分在拖动开始时出现。就我而言,我在 mouseDown() 中执行此操作,但您也可以在 mouseDragged 中执行此操作。
kPasteboardTypeFilePromiseContent)kPasteboardTypeFileURLPromise)kPasteboardTypeFilePromiseContent
这是来自 drop 接收者的第一个回调(通过 pasteboard(pasteboard:item:provideDataForType:))
kPasteboardTypeFilePromiseContent 类型的 UTI(在粘贴板对象上使用 setString(""))进行响应
kPasteboardTypeFileURLPromise
这是接收方的第二次回调(通过pasteboard(pasteboard:item:provideDataForType:))
接收方要求我们将数据写入磁盘上的文件。
com.apple.pastelocation)kPasteboardTypeFileURLPromise 设置写入文件的结果URL(使用粘贴板对象上的setString())来响应。注意,这个字符串的格式需要是file:///...,所以需要使用.absoluteString()。我们完成了!
// Some definitions to help reduce the verbosity of our code
let PasteboardFileURLPromise = NSPasteboard.PasteboardType(rawValue: kPasteboardTypeFileURLPromise)
let PasteboardFilePromiseContent = NSPasteboard.PasteboardType(rawValue: kPasteboardTypeFilePromiseContent)
let PasteboardFilePasteLocation = NSPasteboard.PasteboardType(rawValue: "com.apple.pastelocation")
class MyView: NSView {
override func mouseDown(with event: NSEvent) {
let pasteboardItem = NSPasteboardItem()
// (1, 2) Tell the pasteboard item that we will provide both file and content promises
pasteboardItem.setDataProvider(self, forTypes: [PasteboardFileURLPromise, PasteboardFilePromiseContent])
// Create the dragging item for the drag operation
let draggingItem = NSDraggingItem(pasteboardWriter: pasteboardItem)
draggingItem.setDraggingFrame(self.bounds, contents: image())
// Start the dragging session
beginDraggingSession(with: [draggingItem], event: event, source: self)
}
}
然后,在您的粘贴板项目数据提供程序扩展中...
extension MyView: NSPasteboardItemDataProvider {
func pasteboard(_ pasteboard: NSPasteboard?, item _: NSPasteboardItem, provideDataForType type: NSPasteboard.PasteboardType) {
if type == PasteboardFilePromiseContent {
// The receiver will send this asking for the content type for the drop, to figure out
// whether it wants to/is able to accept the file type (3).
// In my case, I want to be able to drop a file containing PDF from my app onto
// the desktop or another app, so, add the UTI for the pdf (4).
pasteboard?.setString("com.adobe.pdf", forType: PasteboardFilePromiseContent)
}
else if type == PasteboardFileURLPromise {
// The receiver is interested in our data, and is happy with the format that we told it
// about during the kPasteboardTypeFilePromiseContent request.
// The receiver has passed us a URL where we are to write our data to (5).
// It is now waiting for us to respond with a kPasteboardTypeFileURLPromise
guard let str = pasteboard?.string(forType: PasteboardFilePasteLocation),
let destinationFolderURL = URL(string: str) else {
// ERROR:- Receiver didn't tell us where to put the file?
return
}
// Here, we build the file destination using the receivers destination URL
// NOTE: - you need to manage duplicate filenames yourself!
let destinationFileURL = destinationFolderURL.appendingPathComponent("dropped_file.pdf")
// Write your data to the destination file (6). Do better error handling here!
let pdfData = self.dataWithPDF(inside: self.bounds)
try? pdfData.write(to: destinationFileURL, options: .atomicWrite)
// And finally, tell the receiver where we wrote our file (7)
pasteboard?.setString(destinationFileURL.absoluteString, forType: PasteboardFileURLPromise)
}
}
如果有人发现此问题或完全不正确,请告诉我!它似乎至少适用于我的应用程序。
正如 Willeke 所指出的,Apple 有一些示例代码用于使用(较新的)NSFilePromiseProvider 机制进行拖放。
我希望我的搜索从 Apple 的开发者页面而不是 Google ? 开始。那好吧!提供的示例是有效的并且仍然有效,所以如果这篇文章可以帮助某人找到有关拖放的更具凝聚力的信息,那就太棒了。
【讨论】: