问题描述
我正在使用DropDelegate处理iOS上SwiftUI中的拖放。我在这里有一个简化的示例应用程序:
https://gist.github.com/jhaungs/6ac2c3a34309b7115eef40925c27b1ab
问题似乎是SwiftUI框架和我的代码之间的交互 。如果您尝试在 dropEntered 或 dropUpdated 调用中从 DropInfo 加载 itemProvider ,则系统永远不会调用 loadData 函数;但是从 performDrop
调用该代码时,以某种方式可以正常工作如果您在 getItem 中设置了断点或添加了打印语句,则可以看到 itemProviders 永远不会为空;它只有一个正确类型的项目提供者(utf8文本)。但是,当从performDrop调用 getItem 时,对其调用 loadobject 只会调用 loadData 。因此,存在一些不透明的内部状态来介导数据传输。
有什么想法为什么这不一致?对我来说似乎是个虫子。
在 dropEntered 和 dropUpdated 中,我想知道我要拖动的内容,因此我可以进行诸如突出显示,设置动画或什至禁止或取消之类的操作下降,但没有反馈。
我也可能做错了。我花了很多时间,这是我最接近它工作的时间。尚不清楚每个项目一个DropDelegate是正确还是明智的。非常缺少该文档,尤其是工作示例。
关于另一个StackOverflow问题,有人建议将当前项目分配给onDrag中的viewmodel变量,但这会创建竞态条件,在该条件下不会始终设置或清除该变量。
SwiftUI | Using onDrag and onDrop to reorder Items within one single LazyGrid?
解决方法
我有点不清楚您在问什么,但如果您只需要在 SwiftUI 中进行拖放操作,这里有一个基于您链接的相同示例的建议:
import SwiftUI
import UniformTypeIdentifiers
struct GridData: Identifiable,Equatable {
let id: String
}
//MARK: - Model
class Model: ObservableObject {
@Published var data: [GridData]
let columns = [
GridItem(.flexible(minimum: 60,maximum: 60))
]
init() {
data = Array(repeating: GridData(id: "0"),count: 50)
for i in 0..<data.count {
data[i] = GridData(id: String("\(i)"))
}
}
}
//MARK: - Grid
struct DemoDragRelocateView: View {
@StateObject private var model = Model()
@State private var dragging: GridData? // I can't reset this when user drops view ins ame location as drag started
@State private var changedView: Bool = false
var body: some View {
VStack {
ScrollView(.vertical) {
LazyVGrid(columns: model.columns,spacing: 5) {
ForEach(model.data) { d in
GridItemView(d: d)
.opacity(dragging?.id == d.id && changedView ? 0 : 1)
.onDrag {
self.dragging = d
changedView = false
return NSItemProvider(object: String(d.id) as NSString)
}
.onDrop(of: [UTType.text],delegate: DragRelocateDelegate(item: d,listData: $model.data,current: $dragging,changedView: $changedView))
}
}.animation(.default,value: model.data)
}
}
.frame(maxWidth:.infinity,maxHeight: .infinity)
.background(Color.gray.edgesIgnoringSafeArea(.all))
.onDrop(of: [UTType.text],delegate: DropOutsideDelegate(current: $dragging,changedView: $changedView))
}
}
struct DragRelocateDelegate: DropDelegate {
let item: GridData
@Binding var listData: [GridData]
@Binding var current: GridData?
@Binding var changedView: Bool
func dropEntered(info: DropInfo) {
if current == nil { current = item }
changedView = true
if item != current {
let from = listData.firstIndex(of: current!)!
let to = listData.firstIndex(of: item)!
if listData[to].id != current!.id {
listData.move(fromOffsets: IndexSet(integer: from),toOffset: to > from ? to + 1 : to)
}
}
}
func dropUpdated(info: DropInfo) -> DropProposal? {
return DropProposal(operation: .move)
}
func performDrop(info: DropInfo) -> Bool {
changedView = false
self.current = nil
return true
}
}
struct DropOutsideDelegate: DropDelegate {
@Binding var current: GridData?
@Binding var changedView: Bool
func dropEntered(info: DropInfo) {
changedView = true
}
func performDrop(info: DropInfo) -> Bool {
changedView = false
current = nil
return true
}
}
//MARK: - GridItem
struct GridItemView: View {
var d: GridData
var body: some View {
VStack {
Text(String(d.id))
.font(.headline)
.foregroundColor(.white)
}
.frame(width: 60,height: 60)
.background(Circle().fill(Color.green))
}
}
```