NSFilePromiseProviderDelegate 是否也需要是 NSView 或 NSViewController?

Does an NSFilePromiseProviderDelegate need to be also an NSView or NSViewController?

我正在学习 AppKit 教程 Supporting Drag and Drop Through File Promises。我下载了演示应用程序。

我试图将 NSFilePromiseProviderDelegate 功能从 ImageCanvasController class(这是一个 NSViewController)提取到一个单独的 class 中。 (请参阅下面的代码片段之前和之后。)

在我进行更改之前,将图像从应用程序 canvas 拖出到 Finder 和 Apple Notes 中效果很好。但是在我更改之后,当我拖入 Notes 时没有任何反应,而当我拖入 Finder 时出现此错误:

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

是否有任何我需要添加到新 class 中的未记录的协议一致性?或者是否存在某些 NSFilePromiseProviderDelegate 仅在它也是 NSViewNSViewController 时才起作用的基本逻辑?在我在网上找到的所有指南中,它总是与视图相关联,但我没有发现任何必须如此的警告。

注意: 我想将 promise-provider 功能与视图分开的原因是,这样我可以向 beginDraggingSession 提供多个 NSDraggingItem 对象。 For example, when multiple items are selected, and there is a mouseDragged event on one of them, I could start a dragging session including all the selected items.

之前的代码

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
    }
}

Does an NSFilePromiseProviderDelegate need to be also an NSView or 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
    }
}