admin管理员组

文章数量:1122846

I want to implement drag and drop in a LazyVGrid and created the following view (drag'n'drop doesn't work here as I reduced the code example to a minimum in order to focus on the second call of the onDrop closure):

struct ContentView: View {
    
    @State var items: [ItemModel] = (0..<30).map { .init(position: $0, id: $0) }
    @State var draggedItem: ItemModel?
    
    var body: some View {
        ScrollView {
            LazyVGrid(columns: [.init(.fixed(100)), .init(.fixed(100)), .init(.fixed(100))]) {
                ForEach(items) { item in
                    print("item", item.id)
                    return ZStack {
                        RoundedRectangle(cornerRadius: 8)
                            .frame(maxWidth: .infinity)
                            .foregroundStyle(.mint)
                        Text("\(item.position)")
                            .padding()
                    }
                    .onDrop(of: [.plainText], delegate: ItemDropDelegate())
                    .onDrag {
                        // This is called a second time *after* the drop has completed
                        print("––– onDrag –––", item.id)
                        draggedItem = item
                        return NSItemProvider(object: String(item.id) as NSString)
                    }
                }
            }
        }
        .onChange(of: draggedItem) { oldValue, newValue in
            print("draggedItem:", newValue!.id)
        }
    }
}

with this simple model type:

struct ItemModel: Identifiable, Hashable {
    var position: Int
    var id: Int
}

and the most basic DropDelegate one might imagine:

struct ItemDropDelegate: DropDelegate {
    func performDrop(info: DropInfo) -> Bool {
        print("Drop performed")
        // In my full implementation, 
        // I set the view's draggedItem to nil here through a binding.
        // → Excluded that logic from this example to keep it simple.
        return false
    }
}

I would expect the onDrag closure to be called exactly once when the user initiates the drag gesture on an item. But I've observed by the use of a print statement that it's actually called twice: the second time happens roughly a second after the user has already released the dragged item and dropped it on the grid.

As a result, the draggedItem is assigned an ItemModel value and is not nil despite nothing being dragged anymore.

Why is this happening and how can I fix it?


(Note: I asked this question before but had missed to share the code for the ItemModel and the ItemDropDelegate which is now included.)

本文标签: drag and dropWhy is the onDrag closure called after dropping an item in SwiftUIStack Overflow