'How can I set the Dragging Image in SwiftUI on macOS

I am trying to change the preview image for drag and drop from the default for a GridView which seems to include all the visible items in the grid.

As I understand it I should be able to set the NSItemProvider.previewImageHandler block to customize the image that gets used.

I can't seem to find any examples of what such a block would look like in order to return a custom image. Can anyone provide an example of such a block returning an Image.

In addition is it also necessary to call the loadPreviewImage() method on the NSItemProvider - I assume not and that macOS will do this.

Here is what I have so far but this previewHandler is not getting called.

.onDrag {
    let itemProvider = NSItemProvider(object: fileService )
    itemProvider.previewImageHandler = {completion, expectedClass, options in
        os_log("previewImageHandler called")
        let image = NSImage(named: "film")
        if let data = image?.tiffRepresentation {
            completion?(data as NSSecureCoding, nil)
        } else {
            let error = NSError(domain:"", code:001, userInfo:[ NSLocalizedDescriptionKey: "Unable to create preview image"])
            completion?(nil, error)
        }
    }
    return itemProvider
}


Solution 1:[1]

In MacOS 12 and iOS 15, the onDrag modifier has an initialiser with a preview that accepts a ViewBuilder closure to provide a preview image.

Here is an example.

.onDrag({
    let itemProvider = NSItemProvider(object: fileService )
    itemProvider.previewImageHandler = {completion, expectedClass, options in
        os_log("previewImageHandler called")
        let image = NSImage(named: "film")
        if let data = image?.tiffRepresentation {
            completion?(data as NSSecureCoding, nil)
        } else {
            let error = NSError(domain:"", code:001, userInfo:[ NSLocalizedDescriptionKey: "Unable to create preview image"])
            completion?(nil, error)
        }
    }
    return itemProvider

}, preview: {
    Image("myImage")
        .resizable()
        .aspectRatio(contentMode: .fit)
        .frame(width: 64, height: 64)
        .foregroundColor(.teal)
        .padding()
})

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1