Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI View to NSImage in AppKit

SwiftUI 2.0 | Swift 5.4 | Xcode 12.4 | macOS Big Sur 11.4

In iOS there is a way to render SwiftUI views to a UIImage that doesn't work in AppKit, only UIKit:

extension View {
    func snapshot() -> UIImage {
        let controller = UIHostingController(rootView: self)
        let view = controller.view

        let targetSize = controller.view.intrinsicContentSize
        view?.bounds = CGRect(origin: .zero, size: targetSize)
        view?.backgroundColor = .clear

        let renderer = UIGraphicsImageRenderer(size: targetSize)

        return renderer.image { _ in
            view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
        }
    }
}

Is there any workaround to get that working on AppKit? The method UIGraphicsImageRenderer(size:) doesn't work on AppKit and there is no equivalent.

like image 492
AlbertUI Avatar asked Apr 25 '21 19:04

AlbertUI


1 Answers

If I'm understanding your question correctly, you should be able to use an NSBitmapImageRef to do the trick!

Here's a simple example of generating an NSImage from SwiftUI view (Note: your host view must be visible!):


let view = MyView() // some SwiftUI view
let host = NSHostingView(rootView: view)

// Note: the host must be visible on screen, which I am guessing you already have it that way. If not, do that here.

let bitmapRep = host.bitmapImageRepForCachingDisplay(in: host.bounds)
host.cacheDisplay(in: host.bounds, to: bitmapRep!)

let image = NSImage(size: host.frame.size)
image.addRepresentation(bitmapRep!)

Here's an extension to NSHostingView that should safely make snapshots:

extension NSHostingView {
    func snapshot() -> NSImage? {
        // Make sure the view is visible:
        guard self.window != nil else { return nil }

        // Get bitmap data:
        let bitmapRep = self.bitmapImageRepForCachingDisplay(in:
        self.bounds)
        self.cacheDisplay(in: self.bounds, to: bitmapRep!)

        // Create NSImage from NSBitmapImageRep:
        let image = NSImage(size: self.frame.size)
        image.addRepresentation(bitmapRep!)
        
        return image
    }
}

I hope this helps. Let me know if you have any questions!

like image 77
Quinn Avatar answered Oct 30 '22 06:10

Quinn