You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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、邮件类应用时表现正常,效果如下:
将DraggableImage拖拽到TextEdit文档

但我无法将图片拖拽到Finder窗口这类基于文件的应用中,效果如下:
将DraggableImage拖拽到Finder窗口

拖拽到桌面(本质是全屏Finder窗口)也有同样的问题:
将DraggableImage拖拽到桌面

请问我的实现中遗漏了哪些部分?

注:完整演示项目已托管在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 01:06:03