【问题标题】:SwiftUI drag/drop item provider not called consistently by the system系统未一致调用 SwiftUI 拖放项提供程序
【发布时间】:2020-11-07 01:36:21
【问题描述】:

我正在使用 DropDelegate 在 iOS 上的 SwiftUI 中处理拖放操作。我在这里有一个简化的示例应用程序:

https://gist.github.com/jhaungs/6ac2c3a34309b7115eef40925c27b1ab

问题似乎是 SwiftUI 框架和我的代码之间的交互。如果您尝试在 dropEntereddropUpdated 调用中从 DropInfo 加载 itemProvider,系统永远不会调用 DropInfo模型上的strong>loadData函数;但不知何故,当从 performDrop.

调用相同的代码时,它可以正常工作

如果在getItem中设置断点或添加打印语句,可以看到itemProviders永远不会为空;它只有一个正确类型的项目提供者(utf8 文本)。但是在其上调用 loadObject 只会在从 performDrop 调用 getItem 时调用 loadData,。所以有一些不透明的内部状态来调解数据传输。

任何想法为什么这不一致?对我来说这似乎是一个错误。

dropEntereddropUpdated 中,我想知道我在拖动什么,这样我就可以做一些事情,比如突出显示它,或动画它,甚至可能禁止或取消下降,但没有反馈。

我也可能做错了。我花了很多时间在它上面,这是我最接近它的工作。尚不清楚每个项目一个 DropDelegate 是否正确或明智。文档非常缺乏,尤其是工作示例。

关于另一个 StackOverflow 问题的建议是将当前项目分配给 onDrag 中的 viewModel 变量,但这会产生变量未一致设置或清除的竞争条件。

SwiftUI | Using onDrag and onDrop to reorder Items within one single LazyGrid?

【问题讨论】:

  • 你发现了吗?我也有类似的问题。
  • 写完这篇文章后不久,我就放弃了针对这个特定用例的拖放操作,并以不同的方式进行了操作(弹出了一个移动目标列表以供选择)。但据我所知,实际放置可能是一项非常昂贵的操作,例如,复制一组文件,因此在整个拖动结束时调用 performDrop 之前它实际上并没有进行放置/drop 用户承诺实际进行放置时的循环。
  • 我想这确实可以。希望随着 SwiftUI 的成熟,某些不能发生这种情况的用例将得到改进(例如预览布局更改)。

标签: ios swiftui drag-and-drop delegates


【解决方案1】:

我有点不清楚你在问什么,但如果你只需要在 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))
    }
}
```

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多