ios - 系统未一致调用 SwiftUI 拖放项提供程序
问题描述
我正在使用 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 中工作的拖放,这里有一个基于你链接的相同示例的建议:
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))
}
}
```
推荐阅读
- sql - 从 Linq Query 管理空白字段
- c# - 如何从 appsettings.js 加载自定义设置
- dart - Flutter 中的 Round CachedNetworkImage
- python - PyTorch 0.40 优化问题
- android - 是否有与 JavaFX 的 Timeline 类等效的 Android?
- javascript - 为什么他写movie.haveWatched、movie.title、movie.rating?
- python - Python 递归函数 - 引用当前和以前的参数
- arrays - nodejs循环数组创建对象
- php - 将自定义帖子类型循环限制为 1 个结果
- reactjs - 从 heroku 应用上传数据失败。CORS 错误