macOS SwiftUI如何实现将内存NSImage拖拽到Finder等第三方应用
提问内容
我此前尝试通过谷歌搜索解决方案,但搜索结果均为如何接收来自其他应用的拖入内容、或是单应用内的拖拽实现,且大部分内容针对iOS,macOS相关参考资料非常少。
我正在开发一款macOS工具栏应用,需要实现将程序编程生成、仅存于内存中的NSImage拖拽到外部第三方应用的功能。为了让图片支持拖拽,我自定义了DraggableImage结构体,用于替换SwiftUI视图中的Image组件:
struct DraggableImage: View { let image: NSImage var body: some View { Image(nsImage: image) .resizable() .scaledToFit() .padding() .onDrag { guard let tiffRepresentation = image.tiffRepresentation, let bitmapImage = NSBitmapImageRep(data: tiffRepresentation), let bitmapRepresentation = bitmapImage.representation(using: .png, properties: [:]) else { return NSItemProvider(item: image.tiffRepresentation as NSSecureCoding?, typeIdentifier: kUTTypeTIFF as String) } let provider = NSItemProvider(item: bitmapRepresentation as NSSecureCoding?, typeIdentifier: kUTTypePNG as String) provider.previewImageHandler = { (handler, _, _) -> Void in handler?(bitmapRepresentation as NSSecureCoding?, nil) } return provider } } }
目前该DraggableImage结构体的实现,在将图片拖拽到TextEdit、邮件类应用时表现正常,效果如下:
但我无法将图片拖拽到Finder窗口这类基于文件的应用中,效果如下:
拖拽到桌面(本质是全屏Finder窗口)也有同样的问题:
请问我的实现中遗漏了哪些部分?
注:完整演示项目已托管在GitHub,感兴趣的开发者可查看完整实现。
解答
你的实现只向NSItemProvider注册了图片二进制数据类型,而Finder等文件类应用接收拖拽时,需要识别到文件URL类型的标识才能判定为可写入本地的文件对象,这是你当前缺失的核心部分。
修改方案为:先将内存中的PNG数据写入应用临时目录生成临时文件,再将临时文件的URL同时注册到NSItemProvider中,即可同时兼容两类应用的拖拽接收逻辑,修改后完整代码如下:
import SwiftUI import UniformTypeIdentifiers struct DraggableImage: View { let image: NSImage // 自定义导出的文件名,可按需修改 let exportFileName: String = "导出图片.png" var body: some View { Image(nsImage: image) .resizable() .scaledToFit() .padding() .onDrag { guard let tiffRepresentation = image.tiffRepresentation, let bitmapImage = NSBitmapImageRep(data: tiffRepresentation), let pngData = bitmapImage.representation(using: .png, properties: [:]) else { return NSItemProvider(item: image.tiffRepresentation as NSSecureCoding?, typeIdentifier: UTType.tiff.identifier) } let provider = NSItemProvider() // 注册PNG二进制数据,供TextEdit、邮件等应用使用 provider.registerItem(forTypeIdentifier: UTType.png.identifier, visibility: .all) { completionHandler, expectedValueClass, options in completionHandler(pngData as NSSecureCoding?, nil) return nil } // 注册临时文件URL,供Finder等文件类应用使用 provider.registerItem(forTypeIdentifier: UTType.fileURL.identifier, visibility: .all) { completionHandler, expectedValueClass, options in let tempDir = FileManager.default.temporaryDirectory let tempFileURL = tempDir.appendingPathComponent(exportFileName) do { try pngData.write(to: tempFileURL) completionHandler(tempFileURL as NSSecureCoding?, nil) } catch { completionHandler(nil, error) } return nil } // 保留拖拽预览设置 provider.previewImageHandler = { handler, _, _ in handler?(pngData as NSSecureCoding?, nil) } return provider } } }
- 上述实现同时注册了两种类型的拖拽数据,不会影响原有适配文本类应用的拖拽逻辑
- 临时文件会由系统自动管理生命周期,无需手动删除
- 如果需要自定义导出文件的格式、文件名,直接修改
exportFileName和图片生成时的格式参数即可。
内容的提问来源于stack exchange,提问作者weyhan
相关产品推荐
相关产品推荐

