问题描述
我已经查看了 Stack Overflow 上关于使用 SwiftUI 进行拖放重新排序的一些问题,这个问题特别有帮助:SwiftUI | Using onDrag and onDrop to reorder Items within one single LazyGrid?
我希望扩展此功能,我可以在我的 SwifUI 应用程序中将某些内容从一个项目列表拖到另一个项目列表中。假设我有一个 Task
列表:
//TaskView.swift
ScrollView{
vstack{
ForEach(model.tasks,id: \.self){ task in
Text(task.name)
.onDrag{
NSItemProvider(object: String(task.id) as Nsstring)
}
}
}
}
...而且我还有一个 Project
列表,我可以将 Task
拖到该列表上以将其移动到该项目:
//ProjectView.swift
ScrollView{
vstack{
ForEach(model.projects,id: \.self){ project in
Text(project.name)
.onDrop(of: [UTType.text],delegate: ProjectDropDelegate(project: project))
}
}
}
我正在努力解决的部分是在我的 ProjectDropDelegate
中,我试图确定一些事情:
- 什么东西被扔在我身上? (一定是任务)
- 如果是任务,它的
id
是什么,以便我可以对其采取行动? (或者,理想情况下,我可以使用整个Task
对象)
我不知道如何让 NSItemProvider
中的 .onDrag
使用字符串以外的任何内容,并且仍然使用我的 SwiftUI 拖/放功能。就其价值而言,我的 Task
和 Project
对象是核心数据类。
如何让 NSItemProvider
包含键值对,以便我可以传递类型标识符字符串,例如 myapp.task
(对于上面的 #1)和 id
(对于 #2)?
解决方法
经过进一步调查,我找到了一种更简单的方法来处理这一切。如果您需要做的只是将数据从应用程序的一个部分移动到另一个部分,我认为 NSItemProvider
有点不合适。以下是我的处理方式,它似乎效果很好。
我在生成任务列表时提到了 model.tasks
。以下是更多相关信息:
class TaskModel: ObservableObject {
static let shared = TaskModel()
@Published var tasks = [Task]()
var draggedTask: Task? //<-- I added this
//...
}
我向我的模型添加了一个 draggedTask
可选,然后在我的 onDrag
修饰符中设置它,如下所示:
Text(task.name)
.onDrag{
model.draggedTask = task
NSItemProvider(object: NSString())
}
我只是将一个空的 String
对象传递给 NSItemProvider
以满足其拖动某些东西的要求。然后在我的 ProjectDropDelegate
中,我可以拥有我需要的所有东西,包括设置悬停 UI 状态:
import SwiftUI
import UniformTypeIdentifiers
struct ProjectDropDelegate: DropDelegate {
@Binding var hovered: Bool
var project: Project?
var modelTask = TaskModel.shared
//MARK: Check before we start
func validateDrop(info: DropInfo) -> Bool {
//Allow the drop to begin with any String set as the NSItemProvider
return info.hasItemsConforming(to: [UTType.text])
}
//MARK: Drop UI State
func dropEntered(info: DropInfo) {
//Show the hovered state if we have a draggedTask
hovered = modelTask.draggedTask != nil
}
func dropExited(info: DropInfo) {
hovered = false
}
//MARK: Drop and Save
func performDrop(info: DropInfo) -> Bool {
if let task = modelTask.draggedTask{
//Save my task using modelTask...
return true
}else{
return false
}
}
}
这比我最初制作的要简单得多。