3

I am dragging and dropping views using a DropDelegate in SwiftUI. I'm successfully wrapping my data in an NSItemProvider using the .onDrag View Modifier, and I even have .onDrop working if I am able to have my drop data be one of the stored properties of my DropDelegate.

I'm trying to allow for decoding my drop data using the provided DropInfo. I update my view in the dropEntered(info:) delegate method, so that the user can have a preview of their drop before it occurs. When I write

info.itemProviders.first?.loadObject(...) { reading, error in
...
}

The completion handler is not called. If I were to instead write this closure in the performDrop(info:) delegate method, the completion handler would be called. Why is the completion handler only called in performDrop(info:)? Is there a way to have drag & drop previews while performing the necessary changes in real time while not having the data model changed in dropEntered(info:)?

I don't love that I edit my data model in dropEntered(info:), and I haven't gotten to how it would work if the user were to cancel the drag and drop somehow... Perhaps there's a better way to go about this that will allow me to edit my data model in performDrop(info:)?

Thank you!

Edit

Here's the code to reproduce the bug:

struct ReorderableForEach<Content: View, Item: Identifiable>: View {
    let items: [Item]
    let content: (Item) -> Content
    
    var body: some View {
        ForEach(items) { item in
            content(item)
                .onDrag {
                    return NSItemProvider(object: "\(item.id)" as NSString)
                }
                .onDrop(
                    of: [.text],
                    delegate: DragRelocateDelegate()
                )
        }
    }
}

struct DragRelocateDelegate: DropDelegate {
    func dropEntered(info: DropInfo) {
        let _ = info.itemProviders(for: [.text]).first?.loadObject(ofClass: String.self) { item, error in
            print("dropEntered: \(item)") // Won't trigger this
        }
    }
    
    func performDrop(info: DropInfo) -> Bool {
        let _ = info.itemProviders(for: [.text]).first?.loadObject(ofClass: String.self) { item, error in
            print("performDrop: \(item)") // Will trigger this
        }
        
        return true
    }
}
7
  • Needed minimal reproducible example. Commented Apr 28, 2021 at 5:24
  • That's odd. I decode and update UI from dropEntered on macOS successfully (using loadItem). Can you show your NSItemProvider registration/decoding and dropEntered body? Commented Apr 30, 2021 at 22:15
  • @Asperi hi, I have the same issue and I edited the post with the bug reproducible code Commented Aug 31, 2021 at 13:47
  • Does this answer your question stackoverflow.com/a/63438481/12299030? Commented Aug 31, 2021 at 14:36
  • 1
    @viedev no, unfortunately not Commented May 4, 2023 at 9:56

1 Answer 1

0

Updating for those that might find this issue.

I learned that the reason for not providing the data during the dragEntered/dragUpdated is that the user might not have provided consent, so the drag have just accidentally entered your app.

So this is why it is not possible to get this data.

For my in-app transfer, I ended up using a side communications channel, it is not great, but I only needed this for a preview.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.