Перетащите обещание файла из NSView на рабочий стол или другое приложение (macOS) (решено) - PullRequest
1 голос
/ 03 ноября 2019

Мне нужно иметь возможность перетаскивать представление файла (в моем случае это pdf-файл) из NSView, содержащегося в моем приложении, на рабочий стол или другое приложение, которое поддерживает открытие файлов PDF.

Я провел несколько часовпытаясь заставить это работать в моем собственном приложении, и я подумал, что добавлю свое решение сюда, так как в сети много полу-решений, некоторые из которых основаны на расширениях Obj-C, а другие устарели и больше не поддерживаются. Я надеюсь, что эта статья будет такой, какую я бы хотел найти во время моих собственных поисков. Мне также известны все мелочи системы (например, использование файловых координаторов вместо прямой записи), но это, кажется, минимальный код, необходимый для реализации.

Я также предоставилпростая реализация Swift NSView.

Операция выполняется в три основных этапа.

Базовый обзор

Вам необходимо сделать свое представление (или другой элемент управления) «Поставщиком данных». для перетаскивания путем реализации протокола NSPasteboardItemDataProvider. Большая часть требуемой работы (кроме запуска перетаскивания) выполняется в следующей функции протокола.

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

Запуск перетаскивания

Этот раздел выполняется при запуске перетаскивания. В моем случае я делал это в mouseDown (), но вы также можете сделать это, например, в mouseDragged.

  1. Скажите монтажной панели, что мы предоставим тип файла UTI для отбрасывания (kPasteboardTypeFilePromiseContent)
  2. Сообщите монтажной панели, что мы предоставим обещание файла (kPasteboardTypeFileURLPromise) для типа данных, указанного в (1)

Ответ получателю с запросом содержимогочто мы предоставим

kPasteboardTypeFilePromiseContent

Это первый обратный вызов от получателя отбрасывания (через pasteboard(pasteboard:item:provideDataForType:))

Получатель спрашивает нас, какой тип (UTI) файла мы предоставим. Ответьте, установив UTI (используя setString ("" на объекте монтажной панели) для типа kPasteboardTypeFilePromiseContent

Ответ получателю о запросе файла

kPasteboardTypeFileURLPromise

Это второй обратный вызов от получателя (через pasteboard(pasteboard:item:provideDataForType:)). Получатель просит насзаписать данные в файл на диске.

Получатель сообщает нам папку для записи нашего содержимого (com.apple.pastelocation) Записывает данные на диск внутри папки, о которой нам сообщил получатель. Ответьте, установиврезультирующий URL записанного файла (с помощью setString () на объекте из монтажной области) для типа kPasteboardTypeFileURLPromise. Обратите внимание, что формат этой строки должен быть 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)
   }
}

Если кто-то обнаружит проблемы с этим или он совершенно неверный, пожалуйста, дайте мне знать! Похоже, это работает для моего приложения, по крайней мере.

1 Ответ

0 голосов
/ 04 ноября 2019

Как отметил Виллек, у Apple есть пример кода для использования (более нового) механизма NSFilePromiseProvider для перетаскивания.

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

Я хотел бы, чтобы мой поиск начался у разработчика Appleстраницы вместо Google ?. Ну что ж! Предоставленный образец действителен и все еще работает, поэтому, если этот пост поможет кому-то найти более сплоченную информацию о перетаскивании, чем просто фантастика.

...