3

我在 Stack Overflow 上查看了一些关于使用 SwiftUI 进行拖放重新排序的问题,这个问题特别有用:SwiftUI | 使用 onDrag 和 onDrop 在一个 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试图确定几件事的地方:

  1. 什么样的物体落在我身上?(必须是任务)
  2. 如果它是一项任务,它是什么,id所以我可以对其采取行动?(或者,理想情况下,我会让整个Task对象都可以使用)

我不知道如何让我使用除字符串以外的任何东西NSItemProvider.onDrag并且仍然可以使用我的 SwiftUI 拖放功能。对于它的价值,我TaskProject对象是核心数据类。

如何使NSItemProvider包含键值对,以便我可以传递一个类型标识符字符串,如myapp.task(对于上面的#1)和一个id(对于#2)?

4

2 回答 2

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
    }
  }
}

这比我最初制作的要简单得多。

于 2021-03-11T21:56:55.153 回答
0

遇到同样的问题。像这样的东西会起作用:

NSItemProvider(item: letter as NSString, typeIdentifier: "public.plain-text")

这不起作用:

NSItemProvider(item: letter as NSString, typeIdentifier: "com.myapp.mytype")

错误或功能?特征!

第二个自定义类型标识符不起作用的原因是该 UTI 尚未在任何地方声明(尚未)。

解决方案是将您的自定义类型声明为 UTI:

  1. 打开您的项目 > 目标 > 信息 > 导出的类型标识符
  2. 为您的类型添加一个条目:
    • 标识符:com.myapp.mytype
    • 符合:public.data

现在这将起作用(并且您可以提交任何支持的对象NSSecureCoding):

NSItemProvider(item: letter as NSString, typeIdentifier: "com.myapp.mytype")

构建并运行您的应用程序并重新排序您的列表现在可以工作了!

于 2022-01-14T13:24:05.090 回答