SwiftUI 拖放文件
Posted
技术标签:
【中文标题】SwiftUI 拖放文件【英文标题】:SwiftUI Drag and Drop files 【发布时间】:2020-03-24 12:41:46 【问题描述】:我正在尝试向我的 SwiftUI Mac 应用程序添加“拖放”手势/功能。
我想将文件从我的系统/桌面拖放到我的应用程序中。我发现它在常规 Swift 中是可能的。我现在正在尝试在 SwiftUI 中执行此操作。
我在 SwiftUI for Views 中找到了一个 onDrop()
函数。但是,看起来这仅适用于我的应用程序中的内部手势。我想从外面拖拽文件。
在 Swift 中,您需要为拖动的类型注册 NSView。
registerForDraggedTypes([kUTTypeFileURL,kUTTypeImage])
我想创建一个 NSViewRepresentable 并将其包装到我的 SwiftUI 视图中。
这是我想出的代码,但是我不能调用registerForDraggedTyped
。
final class DragDropView: NSViewRepresentable
func makeNSView(context: NSViewRepresentableContext<DragDropView>) -> NSView
let view = NSView()
view.registerForDraggedTypes([NSPasteboard.PasteboardType.pdf, NSPasteboard.PasteboardType.png])
return view
func updateNSView(_ nsView: NSView, context: NSViewRepresentableContext<DragDropView>)
在 SwiftUI 中是否有更简单的解决方案?我很想使用那个 onDrop() 函数,但这不适用于外部文件,是吗?
【问题讨论】:
考虑alfianlosari.com/posts/… 【参考方案1】:这是一个拖放演示,使用 Xcode 11.4 / macOS 10.15.4 进行了测试。
初始 image
位于资产库中,接受拖放(仅为简单起见)作为来自 Finder/Desktop(拖放)和 TextEdit(拖动)的文件 url,为 TIFF 表示注册拖放。
struct TestImageDragDrop: View
@State var image = NSImage(named: "image")
@State private var dragOver = false
var body: some View
Image(nsImage: image ?? NSImage())
.onDrop(of: ["public.file-url"], isTargeted: $dragOver) providers -> Bool in
providers.first?.loadDataRepresentation(forTypeIdentifier: "public.file-url", completionHandler: (data, error) in
if let data = data, let path = NSString(data: data, encoding: 4), let url = URL(string: path as String)
let image = NSImage(contentsOf: url)
DispatchQueue.main.async
self.image = image
)
return true
.onDrag
let data = self.image?.tiffRepresentation
let provider = NSItemProvider(item: data as NSSecureCoding?, typeIdentifier: kUTTypeTIFF as String)
provider.previewImageHandler = (handler, _, _) -> Void in
handler?(data as NSSecureCoding?, nil)
return provider
.border(dragOver ? Color.red : Color.clear)
【讨论】:
Asperi 你太棒了.. 我要怎么感谢你?! :) 来自德国的问候 @Asperi - 我正在尝试在 macOS 11 中使用它,并且 previewImageHander 永远不会被调用 - 知道为什么会这样吗?【参考方案2】:正如 OP 所述,这仅适用于 Mac OS 应用程序。
如果你愿意
-
从 Finder 打开
从 Finder 中拖动
并从其他地方(如网站或 iMessage)拖动
然后试试这个(不包括从这个视图拖动图像)。
XCode 11+ / SwiftUI 1.0+ / Swift 5:
从 Finder 打开所需的扩展:
extension NSOpenPanel
static func openImage(completion: @escaping (_ result: Result<NSImage, Error>) -> ())
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseFiles = true
panel.canChooseDirectories = false
panel.allowedFileTypes = ["jpg", "jpeg", "png", "heic"]
panel.canChooseFiles = true
panel.begin (result) in
if result == .OK,
let url = panel.urls.first,
let image = NSImage(contentsOf: url)
completion(.success(image))
else
completion(.failure(
NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to get file location"])
))
SwiftUI 视图
struct InputView: View
@Binding var image: NSImage?
var body: some View
VStack(spacing: 16)
HStack
Text("Input Image (PNG,JPG,JPEG,HEIC)")
Button(action: selectFile)
Text("From Finder")
InputImageView(image: self.$image)
private func selectFile()
NSOpenPanel.openImage (result) in
if case let .success(image) = result
self.image = image
struct InputImageView: View
@Binding var image: NSImage?
var body: some View
ZStack
if self.image != nil
Image(nsImage: self.image!)
.resizable()
.aspectRatio(contentMode: .fit)
else
Text("Drag and drop image file")
.frame(width: 320)
.frame(height: 320)
.background(Color.black.opacity(0.5))
.cornerRadius(8)
.onDrop(of: ["public.url","public.file-url"], isTargeted: nil) (items) -> Bool in
if let item = items.first
if let identifier = item.registeredTypeIdentifiers.first
print("onDrop with identifier = \(identifier)")
if identifier == "public.url" || identifier == "public.file-url"
item.loadItem(forTypeIdentifier: identifier, options: nil) (urlData, error) in
DispatchQueue.main.async
if let urlData = urlData as? Data
let urll = NSURL(absoluteURLWithDataRepresentation: urlData, relativeTo: nil) as URL
if let img = NSImage(contentsOf: urll)
self.image = img
print("got it")
return true
else print("item not here") return false
注意:不需要使用“public.image”标识符。
如果您需要将结果作为 PNG 数据的可选扩展(我已上传到 Firebase 存储):
extension NSBitmapImageRep
var png: Data? representation(using: .png, properties: [.compressionFactor:0.05])
extension Data
var bitmap: NSBitmapImageRep? NSBitmapImageRep(data: self)
extension NSImage
var png: Data? tiffRepresentation?.bitmap?.png
// usage
let image = NSImage(...)
if let data = image.png
// do something further with the data
【讨论】:
【参考方案3】:你不能用onDrop(of:isTargeted:perform:)吗?您可以在 of
参数中传递您支持的类型数组。
【讨论】:
感谢您的回答 :) 我试过了,但我认为它不起作用。这应该适用于外部文件吗?我会再试一次,一秒钟 是这样使用的 .onDrop(of: ["png", "pdf", "jpg", "jpeg"], isTargeted: $target, perform: onFileDrop) 但是如果我拖动文件在它上面,什么都没有发生。我也没有得到那个鼠标加指示 正如 Asperi 上面所说,onDrop 实际上也像您所说的那样工作。我会检查我做错了什么。感谢您的帮助!! 它应该像.onDrop(of: [.png, .pdf, .jpeg]
,因为你需要在那里作为UTType。以上是关于SwiftUI 拖放文件的主要内容,如果未能解决你的问题,请参考以下文章
UICollectionView 与 SwiftUI + 拖放重新排序可能吗?
SwiftUI:如何在 macOS 上从 Mail 中拖放电子邮件