将文件承诺从NSView拖放到桌面或其他应用程序(macOS)

问题描述 投票:1回答:1

我需要能够将应用程序中包含的NSView的文件表示形式(在我的情况下为pdf)拖到桌面或支持打开PDF文件的其他应用程序上。

swift macos drag-and-drop nsview
1个回答
0
投票

我花了几个小时试图在自己的应用程序中运行它,我想在这里添加我的解决方案,因为在线上有很多半解决方案,其中一些依赖于Obj-C扩展,而另一些则依赖于Obj-C扩展。已过时,不再受支持。我希望这篇文章能像我希望在自己的搜索中找到的那样。我也知道系统的所有细节(例如,使用文件协调器而不是直接写入),但这似乎是实现所需的最少代码。

我还提供了一个简单的Swift NSView实现。

该操作分为三个主要阶段。

基本概述

您需要通过实现NSPasteboardItemDataProvider协议,将视图(或其他控件)作为拖动的“数据提供程序”。所需的大部分工作(开始拖动除外)发生在以下协议功能中。

func pasteboard(_ pasteboard: NSPasteboard?, item _: NSPasteboardItem, provideDataForType type: NSPasteboard.PasteboardType)

开始拖动

此部分在拖动开始时发生。就我而言,我是在mouseDown()中执行此操作的,但是例如,您也可以在mouseDragged中执行此操作。

  1. 告诉剪贴板,我们将为放置(kPasteboardTypeFilePromiseContent)提供文件类型UTI
  2. 告诉剪贴板,我们将为(1)中指定的数据类型提供文件保证(kPasteboardTypeFileURLPromise

响应收件人要求我们提供的内容

kPasteboardTypeFilePromiseContent

这是子站的接收者的第一个回调(通过pasteboard(pasteboard:item:provideDataForType:)

  1. 接收者正在询问我们我们将提供哪种类型的文件(UTI)。
  2. 通过为类型kPasteboardTypeFilePromiseContent设置UTI(通过在粘贴板对象上使用setString(“”))来响应

响应收件人要求的文件

kPasteboardTypeFileURLPromise

这是接收方的第二个回调(通过pasteboard(pasteboard:item:provideDataForType:))接收方要求我们将数据写入磁盘上的文件。

  1. 接收者告诉我们要将内容写入(com.apple.pastelocation)的文件夹
  2. 将数据写入接收者告诉我们的文件夹内的磁盘。
  3. 通过为类型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)
   }
}

然后,在您的Pasteboard Item Data提供程序扩展中...

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机制进行拖放。

https://developer.apple.com/documentation/appkit/documents_files_and_icloud/supporting_drag_and_drop_through_file_promises

我希望我的搜索从Apple的Developer页面而不是Google started开始。那好吧!提供的示例有效并且仍然有效,因此,如果这篇文章可以帮助某人找到有关拖放的更多衔接信息,那就太棒了。

© www.soinside.com 2019 - 2024. All rights reserved.