如何在 SwiftUI (macOS) 中拖放未捆绑的图像

     2023-02-24     217

关键词:

【中文标题】如何在 SwiftUI (macOS) 中拖放未捆绑的图像【英文标题】:How to drag and drop images not in bundle in SwiftUI (macOS) 【发布时间】:2020-05-09 23:38:14 【问题描述】:

我正在尝试在 macOS 上的 SwiftUI 中实现拖放,我可以在其中以编程方式生成图像并将它们作为可拖动项显示在视图中,或者出于相同目的从 Assets.xcassets 加载图像。我的代码基于this。我试图参考this question,但我无法让它工作。

我的图像显示正常,但由于我引用的是图像本身,因此我无法为拖放 API 返回 URL(见下文):

//Value of type 'ContentView.DragableImage' has no member 'url' so I cannot use this
.onDrag  return NSItemProvider(object: self.url as NSURL) 

这里是代码。我已经通过代码中的 cmets 指出了一些事情:

import SwiftUI

let img1url = Bundle.main.url(forResource: "grapes", withExtension: "png") // < -- CAN pass this in because it is by url
let img2url = Bundle.main.url(forResource: "banana", withExtension: "png")
let img3url = Bundle.main.url(forResource: "peach", withExtension: "png")
let img4url = Bundle.main.url(forResource: "kiwi", withExtension: "png")

struct ContentView: View 

    var body: some View 
        HStack 
            VStack 
                //DragableImage(url: img1url!)
                //DragableImage(url: img3url!)

                DragableImage()
                DragableImage()
            

            VStack 
                  //DragableImage(url: img2url!)
                 // DragableImage(url: img4url!)

               DragableImage()
               DragableImage()
            

            DroppableArea()
        .padding(40)
    

    struct DragableImage: View 
        //let url: URL

        var body: some View 
            Image("grapes") //<--- Takes in image without url fine
           //Image(nsImage: NSImage(byReferencing: url)) //<--- Taking in image by URL (I don't want that)
                .resizable()
                .frame(width: 150, height: 150)
                .clipShape(Circle())
                .overlay(Circle().stroke(Color.white, lineWidth: 2))
                .padding(2)
                .overlay(Circle().strokeBorder(Color.black.opacity(0.1)))
                .shadow(radius: 3)
                .padding(4)
                .onDrag  return NSItemProvider(object: self.url as NSURL)  //<--- MAIN ISSUE: "Value of type 'ContentView.DragableImage' has no member 'url'" (there is now no URL to reference the image by in the return)

        
    

    struct DroppableArea: View 
        @State private var imageUrls: [Int: URL] = [:]
        @State private var active = 0

        var body: some View 
            let dropDelegate = MyDropDelegate(imageUrls: $imageUrls, active: $active)

            return VStack 
                HStack 
                    GridCell(active: self.active == 1, url: imageUrls[1])

                    GridCell(active: self.active == 3, url: imageUrls[3])
                

                HStack 
                    GridCell(active: self.active == 2, url: imageUrls[2])

                    GridCell(active: self.active == 4, url: imageUrls[4])
                

            
            .background(Rectangle().fill(Color.gray))
            .frame(width: 300, height: 300)
            .onDrop(of: ["public.file-url"], delegate: dropDelegate)

        
    

    struct GridCell: View 
        let active: Bool
        let url: URL?

        var body: some View 
            let img = Image(nsImage: url != nil ? NSImage(byReferencing: url!) : NSImage())
                .resizable()
                .frame(width: 150, height: 150)

            return Rectangle()
                .fill(self.active ? Color.green : Color.clear)
                .frame(width: 150, height: 150)
                .overlay(img)
        
    

    struct MyDropDelegate: DropDelegate 
        @Binding var imageUrls: [Int: URL]
        @Binding var active: Int

        func validateDrop(info: DropInfo) -> Bool 
            return info.hasItemsConforming(to: ["public.file-url"])
        

        func dropEntered(info: DropInfo) 
            NSSound(named: "Morse")?.play()
        

        func performDrop(info: DropInfo) -> Bool 
            NSSound(named: "Submarine")?.play()

            let gridPosition = getGridPosition(location: info.location)
            self.active = gridPosition

            if let item = info.itemProviders(for: ["public.file-url"]).first 
                item.loadItem(forTypeIdentifier: "public.file-url", options: nil)  (urlData, error) in
                    DispatchQueue.main.async 
                        if let urlData = urlData as? Data 
                            self.imageUrls[gridPosition] = NSURL(absoluteURLWithDataRepresentation: urlData, relativeTo: nil) as URL
                        
                    
                

                return true

             else 
                return false
            

        

        func dropUpdated(info: DropInfo) -> DropProposal? 
            self.active = getGridPosition(location: info.location)

            return nil
        

        func dropExited(info: DropInfo) 
            self.active = 0
        

        func getGridPosition(location: CGPoint) -> Int 
            if location.x > 150 && location.y > 150 
                return 4
             else if location.x > 150 && location.y < 150 
                return 3
             else if location.x < 150 && location.y > 150 
                return 2
             else if location.x < 150 && location.y < 150 
                return 1
             else 
                return 0
            
        
    



我还尝试了以下方法并取得了更大的成功。它在图像拖动时突出显示,但在放置时不会显示:

import SwiftUI

let image1 = NSImage(named: "green")!
let image2 = NSImage(named: "blue")!

struct ContentView: View 

    var body: some View 
        HStack 
            VStack 
                DragableImage(image: image1)
                DragableImage(image: image2)
            

            VStack 
               DragableImage(image: image1)
               DragableImage(image: image2)
            

            DroppableArea()
        .padding(40)
    

    struct DragableImage: View 
        @State var image: NSImage
        @State private var dragOver = false

        var body: some View 
            Image(nsImage: image)
                .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 imageLocal = NSImage(contentsOf: url)
                            DispatchQueue.main.async 
                                self.image = imageLocal!
                            
                        
                    )
                    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)
        
    

    struct DroppableArea: View 
        @State private var imageUrls: [Int: URL] = [:]
        @State private var active = 0

        var body: some View 
            let dropDelegate = MyDropDelegate(imageUrls: $imageUrls, active: $active)

            return VStack 
                HStack 
                    GridCell(active: self.active == 1, url: imageUrls[1])

                    GridCell(active: self.active == 3, url: imageUrls[3])
                

                HStack 
                    GridCell(active: self.active == 2, url: imageUrls[2])

                    GridCell(active: self.active == 4, url: imageUrls[4])
                

            
            .background(Rectangle().fill(Color.gray))
            .frame(width: 300, height: 300)
            .onDrop(of: ["public.file-url"], delegate: dropDelegate)

        
    

    struct GridCell: View 
        let active: Bool
        let url: URL?

        var body: some View 
            let img = Image(nsImage: url != nil ? NSImage(byReferencing: url!) : NSImage())
                .resizable()
                .frame(width: 150, height: 150)

            return Rectangle()
                .fill(self.active ? Color.green : Color.clear)
                .frame(width: 150, height: 150)
                .overlay(img)
        
    

    struct MyDropDelegate: DropDelegate 
        @Binding var imageUrls: [Int: URL]
        @Binding var active: Int

        func validateDrop(info: DropInfo) -> Bool 
            return info.hasItemsConforming(to: ["public.file-url"])
        

        func dropEntered(info: DropInfo) 
            NSSound(named: "Morse")?.play()
        

        func performDrop(info: DropInfo) -> Bool 
            NSSound(named: "Submarine")?.play()

            let gridPosition = getGridPosition(location: info.location)
            self.active = gridPosition

            if let item = info.itemProviders(for: ["public.file-url"]).first 
                item.loadItem(forTypeIdentifier: "public.file-url", options: nil)  (urlData, error) in
                    DispatchQueue.main.async 
                        if let urlData = urlData as? Data 
                            self.imageUrls[gridPosition] = NSURL(absoluteURLWithDataRepresentation: urlData, relativeTo: nil) as URL
                        
                    
                

                return true

             else 
                return false
            

        

        func dropUpdated(info: DropInfo) -> DropProposal? 
            self.active = getGridPosition(location: info.location)

            return nil
        

        func dropExited(info: DropInfo) 
            self.active = 0
        

        func getGridPosition(location: CGPoint) -> Int 
            if location.x > 150 && location.y > 150 
                return 4
             else if location.x > 150 && location.y < 150 
                return 3
             else if location.x < 150 && location.y > 150 
                return 2
             else if location.x < 150 && location.y < 150 
                return 1
             else 
                return 0
            
        
    

【问题讨论】:

【参考方案1】:

和参考题几乎一样,关键是用NSImage作为模型,而不是直接使用Image,才能有能力访问图像数据以使用NSItemProvider

struct DragableImage: View 
    var image = NSImage(named: "grapes")

    var body: some View 
        Image(nsImage: image ?? NSImage())
            .resizable()
            .frame(width: 150, height: 150)
            .clipShape(Circle())
            .overlay(Circle().stroke(Color.white, lineWidth: 2))
            .padding(2)
            .overlay(Circle().strokeBorder(Color.black.opacity(0.1)))
            .shadow(radius: 3)
            .padding(4)
            .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
            
    

【讨论】:

唯一的问题是可拖动的图像(绿色和蓝色)不会在放置时显示(它甚至不会播放来自 dropEntered 或 performDrop 的声音 (图像永远不会被添加到 DroppableArea)

SwiftUI:如何从 macOS 上的联系人中拖放联系人

】SwiftUI:如何从macOS上的联系人中拖放联系人【英文标题】:SwiftUI:HowtodraganddropacontactfromContactsonmacOS【发布时间】:2021-02-1117:56:21【问题描述】:我正在尝试将联系人从联系人拖到我的应用程序中。这是我更新的代码:importSwiftU... 查看详情

如何在 Silverlight 中拖放“框”

】如何在Silverlight中拖放“框”【英文标题】:Howtodraganddropa"box"insilverlight【发布时间】:2013-06-0419:00:10【问题描述】:我已经创建了一个这样的盒子,现在我正在尝试拖放带有矩形和其他对象的盒子,但我不知道该怎么... 查看详情

如何在 JTable 中拖放一行?

】如何在JTable中拖放一行?【英文标题】:HowdoIdraganddroparowinaJTable?【发布时间】:2010-10-1222:25:24【问题描述】:如何设置JTable以便能够将行拖动到表中的不同索引。例如,如果我有5行,我想将第4行拖到第2位?【问题讨论】:... 查看详情

如何防止在反应应用程序中拖放图像?

】如何防止在反应应用程序中拖放图像?【英文标题】:HowcanIpreventdraganddropimagesinareactapp?【发布时间】:2018-09-2815:20:28【问题描述】:我们得到了howtopreventdragdropimagewithjquery和$(\'img\').on(\'dragstart\',function(event)event.preventDefault(););... 查看详情

如何在 React 中拖放多个元素?

】如何在React中拖放多个元素?【英文标题】:HowtoDrag&DropMultipleElementsInReact?【发布时间】:2019-09-0901:37:50【问题描述】:这是我在***上的第一个问题。我想用React构建一个小游戏,用户可以将tetrominos拖放到网格上,还可以根... 查看详情

如何在java中从目录中拖放文件

】如何在java中从目录中拖放文件【英文标题】:howtodraganddropfilesfromadirectoryinjava【发布时间】:2012-11-1520:11:36【问题描述】:我想实现从某个目录(例如某人的硬盘驱动器)拖放文件,但不知道该怎么做。我已经阅读了javaapi,... 查看详情

如何在 obj-c 中拖放“.txt”文件

】如何在obj-c中拖放“.txt”文件【英文标题】:HowdoIDragandDropa\'.txt\'fileinobj-c【发布时间】:2011-01-0309:08:33【问题描述】:我正在尝试编写一些绝对准系统的代码,在其中我可以将一个普通的“dot.txt”文件拖到一个NSWindow上并读... 查看详情

如何在图片框中拖放图像[关闭]

】如何在图片框中拖放图像[关闭]【英文标题】:Howtodraganddropanimageinsideapicturebox[closed]【发布时间】:2020-01-1519:06:51【问题描述】:我希望能够将图像拖放到图片框中,我所能找到的只是如何将图像拖放到图片框中,但在这种情... 查看详情

如何使用 jQuery 在 div 中多次拖放图像以及在 drop div 中拖放图像?

】如何使用jQuery在div中多次拖放图像以及在dropdiv中拖放图像?【英文标题】:HowanimagecanbedraggedanddroppedmultipletimeswithindivusingjQueryandalsoimagedroppedwithindroppeddiv?【发布时间】:2020-02-0415:06:05【问题描述】:我的html中有两个div。一个... 查看详情

在嵌套数据表中拖放

...问题描述】:我使用的是primeface4.0。primefaceshowcase仅显示如何将draggable拖入droppable。但是然后如何将draggable拖出那个droppable从未提及。我想制作一个数据表:每个td包含一个droppable面板,其中多个draggable既可以 查看详情

在 WPF 中拖放 ListboxItems

...发布时间】:2012-07-2904:07:18【问题描述】:我试图弄清楚如何通过鼠标拖动来上下移动带有MediaElements的预填充列表框中的项目。我将不胜感激任何帮助。谢谢【问题讨论】:我建议您查看BeaStollnitz\'sarticleondragging/droppingdataboundite... 查看详情

关于如何在 ASP.net 中拖放的建议

】关于如何在ASP.net中拖放的建议【英文标题】:adviceonhowtodrag/dropinASP.net【发布时间】:2013-03-1702:17:09【问题描述】:好的,所以我想要一个简单的SINGLE网页,它可以带一个小人,并将他拖到一个运动场(只是一个背景形状的足... 查看详情

Qt - 在QGraphicScene中拖放时如何从项目中获取文本?

】Qt-在QGraphicScene中拖放时如何从项目中获取文本?【英文标题】:Qt-HowtogetthetextfromanitemwhendragginganddroppinginaQGraphicScene?【发布时间】:2018-05-0915:31:23【问题描述】:我是QtCreator和一般编码的新手,我正在尝试创建一个应用程序,... 查看详情

Android:在 RecyclerView 中拖放项目时如何有效更新 SQLite 数据库?

】Android:在RecyclerView中拖放项目时如何有效更新SQLite数据库?【英文标题】:Android:HowdoIeffectivelyupdatetheSQLitedatabasewhendragginganddroppingitemsintheRecyclerView?【发布时间】:2018-07-2623:35:16【问题描述】:我有这个待办事项列表应用程序... 查看详情

C# 在 Canvas 中拖放图像

...间】:2014-03-1623:46:05【问题描述】:我尝试在Google上搜索如何在Canvas上拖放UIElements,但找不到任何我想要的东西。我有一个带有Window的C#WPF应用程序。在窗口内我有一个画布,我可以在其中添加图像。我想要的是能够拖放图像,... 查看详情

如何在自定义形状(JavaScript、jQuery UI 或 Interact JS)中拖放?

】如何在自定义形状(JavaScript、jQueryUI或InteractJS)中拖放?【英文标题】:HowCanYouDragandDropInsideCustomShapes(JavaScript,jQueryUIorInteractJS)?【发布时间】:2021-10-2809:59:16【问题描述】:我尝试从列表中拖放到十字圈内,如下图所示:但... 查看详情

相关容器的Chrome本机拖放未正确呈现鬼影

】相关容器的Chrome本机拖放未正确呈现鬼影【英文标题】:Chromenativedrag-and-dropofrelativecontainerisnotrenderingghostright【发布时间】:2016-09-0804:36:27【问题描述】:当您希望具有相对位置的&lt;div&gt;可拖动,并且此&lt;div&gt;... 查看详情

从 NSOutlineView 中拖放

...时间】:2012-08-2819:33:00【问题描述】:我正在尝试弄清楚如何实现从NSOutlineView到自身的拖放。例如,我希望用户能够在NSOutlineView中重新排序和嵌套和重新嵌套项目,但我不需要能够从任何其他来源(如文件或其他视图)拖动项... 查看详情