Должен ли NSFilePromiseProviderDelegate также быть NSView или NSViewController?

Я просматриваю учебник AppKit, поддерживающий перетаскивание файловых обещаний . Я скачал демо-приложение.

Я попытался извлечь функциональность из ImageCanvasControllerкласс (который является ) в отдельный класс. (Пожалуйста, смотрите фрагменты кода до и после ниже.)

До моих изменений перетаскивание изображения с холста приложения в Finder и Apple Notes работало нормально. Но после моих изменений ничего не происходит, когда я перетаскиваю в Notes, и я получаю эту ошибку, когда я перетаскиваю в Finder:

2022-02-26 23:16:52.713742+0100 MemeGenerator[31536:1975798] *** CFMessagePort: dropping corrupt reply Mach message (0b000100)

Есть ли какое-либо недокументированное соответствие протоколу, которое мне нужно добавить в новый класс? Или есть какая-то основная логика, для которой NSFilePromiseProviderDelegateработает только если это также NSViewили NSViewController? Во всех руководствах, которые я нашел в Интернете, это всегда привязано к представлению, но я не нашел никакого предупреждения, что это должно быть.

Примечание. Причина, по которой я хочу отделить функциональность промис-провайдера от представлений, заключается в том, что таким образом я могу предоставить несколько NSDraggingItemвозражает против beginDraggingSession. Например, когда выбрано несколько элементов и есть mouseDraggedсобытие на одном из них, я мог начать сеанс перетаскивания, включая все выбранные элементы.

Код перед

      class ImageCanvasController: NSViewController, NSFilePromiseProviderDelegate, ImageCanvasDelegate, NSToolbarDelegate {
   
    ...

    /// Queue used for reading and writing file promises.
    private lazy var workQueue: OperationQueue = {
        let providerQueue = OperationQueue()
        providerQueue.qualityOfService = .userInitiated
        return providerQueue
    }()
    
    ...

    func pasteboardWriter(forImageCanvas imageCanvas: ImageCanvas) -> NSPasteboardWriting {
        let provider = NSFilePromiseProvider(fileType: kUTTypeJPEG as String, delegate: self)
        provider.userInfo = imageCanvas.snapshotItem
        return provider
    }
    
    // MARK: - NSFilePromiseProviderDelegate
    
    /// - Tag: ProvideFileName
    func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, fileNameForType fileType: String) -> String {
        let droppedFileName = NSLocalizedString("DropFileTitle", comment: "")
        return droppedFileName + ".jpg"
    }
    
    /// - Tag: ProvideOperationQueue
    func operationQueue(for filePromiseProvider: NSFilePromiseProvider) -> OperationQueue {
        return workQueue
    }
    
    /// - Tag: PerformFileWriting
    func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, writePromiseTo url: URL, completionHandler: @escaping (Error?) -> Void) {
        do {
            if let snapshot = filePromiseProvider.userInfo as? ImageCanvas.SnapshotItem {
                try snapshot.jpegRepresentation?.write(to: url)
            } else {
                throw RuntimeError.unavailableSnapshot
            }
            completionHandler(nil)
        } catch let error {
            completionHandler(error)
        }
    }
}

Код после

      class CustomFilePromiseProviderDelegate: NSObject, NSFilePromiseProviderDelegate {
    /// Queue used for reading and writing file promises.
    private lazy var workQueue: OperationQueue = {
        let providerQueue = OperationQueue()
        providerQueue.qualityOfService = .userInitiated
        return providerQueue
    }()

    /// - Tag: ProvideFileName
    func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, fileNameForType fileType: String) -> String {
        let droppedFileName = NSLocalizedString("DropFileTitle", comment: "")
        return droppedFileName + ".jpg"
    }

    /// - Tag: ProvideOperationQueue
    func operationQueue(for filePromiseProvider: NSFilePromiseProvider) -> OperationQueue {
        return workQueue
    }

    /// - Tag: PerformFileWriting
    func filePromiseProvider(_ filePromiseProvider: NSFilePromiseProvider, writePromiseTo url: URL, completionHandler: @escaping (Error?) -> Void) {
        do {
            if let snapshot = filePromiseProvider.userInfo as? ImageCanvas.SnapshotItem {
                try snapshot.jpegRepresentation?.write(to: url)
            } else {
                throw RuntimeError.unavailableSnapshot
            }
            completionHandler(nil)
        } catch let error {
            completionHandler(error)
        }
    }
}

class ImageCanvasController: NSViewController, ImageCanvasDelegate, NSToolbarDelegate {

    ...

    func pasteboardWriter(forImageCanvas imageCanvas: ImageCanvas) -> NSPasteboardWriting {
        let delegate = CustomFilePromiseProviderDelegate()
        let provider = NSFilePromiseProvider(fileType: kUTTypeJPEG as String, delegate: delegate)
        provider.userInfo = imageCanvas.snapshotItem
        return provider
    }
}

1 ответ

Должен ли NSFilePromiseProviderDelegate также быть NSView или NSViewController?

Нет.

delegateявляется локальной переменной и освобождается в конце pasteboardWriter(forImageCanvas:). Без делегата поставщик файловых обещаний не работает. Решение: сохраните сильную ссылку на делегата.

      class ImageCanvasController: NSViewController, ImageCanvasDelegate, NSToolbarDelegate {

    let delegate = CustomFilePromiseProviderDelegate()

    ...

    func pasteboardWriter(forImageCanvas imageCanvas: ImageCanvas) -> NSPasteboardWriting {
        let provider = NSFilePromiseProvider(fileType: kUTTypeJPEG as String, delegate: delegate)
        provider.userInfo = imageCanvas.snapshotItem
        return provider
    }
}
Другие вопросы по тегам