如何将单个容器标记为多个 Transferable 类型的 dropDestination?

jde*_*ano 5 drag-and-drop swiftui

我使用新的 Transferable 协议和 Draggable/dropDestination 修饰符来让用户将内容拖放到 ZStack 上。我遇到的问题是我想支持将多个可转移类型放入单个容器中。例如,我希望用户能够将字符串、URL 或数据(即图像数据)拖放到单个 ZStack 上。问题在于 dropDestination 视图修饰符上的“for”参数不接受多个类型,就像 onDrop 修饰符一样。

我尝试添加具有不同有效负载的第二个 dropDestination 修饰符,但是当我放置与第二个放置目标有效负载相对应的项目时,我在拖动的图像上看到一个图标,指示不允许放置。但是,如果我删除一个字符串有效负载,我会像预期的那样得到 + 图标,并且删除成功。

struct ContentView: View {
    
    @State private var stringPayload: String = ""
    @State private var urlPayload: URL?
    
    var body: some View {
        VStack {
            ZStack {
                Color.yellow
                Text(stringPayload)
                if let urlPayload {
                    Image(uiImage: UIImage(data: (try? Data(contentsOf: urlPayload))!)!)
                }
            }
            .dropDestination(for: String.self) { items, location in
                stringPayload = items.first!
                return true
            }
            .dropDestination(for: URL.self) { items, location in
                return true
            }
            Text("Hello world!")
                .draggable("Hello world!")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

jde*_*ano 12

感谢 @user1046037 建议查看 ProxyRepresentation,我能够编写一些代码,使我能够在单个 dropDestination 接收器中接受多种 drop 类型。

首先,我创建了一个单独的枚举来表示可以删除的不同类型的数据:

import CoreTransferable

enum DropItem: Codable, Transferable {
    case none
    case text(String)
    case url(URL)
    
    static var transferRepresentation: some TransferRepresentation {
        ProxyRepresentation { DropItem.text($0) }
        ProxyRepresentation { DropItem.url($0) }
    }
    
    var text: String? {
        switch self {
            case .text(let str): return str
            default: return nil
        }
    }
    
    var url: URL? {
        switch self {
            case.url(let url): return url
            default: return nil
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在视图中,只需告诉 dropDestination 接受 DropItem.self 类型的项目,如下所示:

struct ContentView: View {
    
    @State private var payload: DropItem = .none
    @State private var urlPayload: URL?
    
    var body: some View {
        VStack {
            ZStack {
                Color.yellow
                if let text = payload.text {
                    Text(text)
                } else if let url = payload.url {
                    Text(url.absoluteString)
                }
            }
            .dropDestination(for: DropItem.self) { items, location in
                payload = items.first!
                return true
            }
            Text("Hello world!")
                .draggable("Hello world!")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方法的真正好处在于,您可以依靠强类型 DropItems 来根据收到的枚举中的情况确定要执行的操作。