繁体   English   中英

SwiftUI 拖放文件

[英]SwiftUI Drag and Drop files

我正在尝试向我的 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() 函数,但这不适用于外部文件,是吗?

这是一个拖放演示,在 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)
    }
}

正如 OP 所指出的,这适用于 Mac OS 应用程序。

如果你想

  1. 从 Finder 打开
  2. 从 Finder 中拖动
  3. 并从其他地方(如网站或 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
}

你不能使用onDrop(of:isTargeted:perform:)吗? 您可以在of参数中传递支持类型的数组。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM