Home > Back-end >  SwiftUI onDrag preview doesn't get updated when Image changes on macOS
SwiftUI onDrag preview doesn't get updated when Image changes on macOS

Time:05-28

I'm having an .onDrag on an Image. Whether I provide a preview myself or not, the preview always gets stuck to show the Image that appeared on the first drag. The NSItemProvider itself works well and I access the proper data, but not within the preview.

enter image description here

A simple example (code available here: https://github.com/godbout/DragPreview)

import SwiftUI


struct AppDetail {
    
    let bundleIdentifier: String
    let icon: NSImage
    
    init(bundleIdentifier: String) {
        self.bundleIdentifier = bundleIdentifier
        
        if
            let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleIdentifier),
            let representation = NSWorkspace.shared.icon(forFile: url.path).bestRepresentation(for: NSRect(x: 0, y: 0, width: 64, height: 64), context: nil, hints: nil)
        {
            self.icon = NSImage(size: representation.size)
            self.icon.addRepresentation(representation)
        } else {
            self.icon = NSApp.applicationIconImage!
        }
    }
    
}


struct ContentView: View {

    @State private var apps: [AppDetail] = [
        AppDetail(bundleIdentifier: "com.apple.Mail"),
        AppDetail(bundleIdentifier: "com.apple.MobileSMS"),
        AppDetail(bundleIdentifier: "com.apple.Safari"),
    ]
    
    var body: some View {
        VStack(alignment: .center) {
            Image(nsImage: apps.first == nil ? NSApp.applicationIconImage! : apps.first!.icon)
                .onDrag {
                    guard
                        let app = apps.first,
                        let url = NSWorkspace.shared.urlForApplication(withBundleIdentifier: app.bundleIdentifier)
                    else {
                        return NSItemProvider()
                    }
                                       
                    return NSItemProvider(object: url as NSURL)
                } preview: {
                    Text(apps.first == nil ? "no app" : apps.first!.bundleIdentifier)
                    Image(nsImage: apps.first == nil ? NSApp.applicationIconImage! : apps.first!.icon)
                }
            Button("next") {
                apps.removeFirst()
            }
            .disabled(apps.isEmpty)
        }
        .frame(width: 200, height: 200)
        .padding()   
    }

}

Anything I'm doing wrong? Could this be a(nother) SwiftUI bug? If the preview is actually static and only rendered at the first drag, how could I change the code so that I get the preview of my current Image?

Thanks.

CodePudding user response:

I read somewhere in a comment by @Asperi that the drag preview is only created once at init. So if you add an .id to the image it forces a redraw and reinit of preview:

            Image(nsImage: apps.first == nil ? NSApp.applicationIconImage! : apps.first!.icon)
                .id(apps.first?.bundleIdentifier) // here
                .onDrag { ...
  • Related