9

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.

AlbertUI
  • 1,409
  • 9
  • 26

1 Answers1

1

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!

Quinn
  • 26
  • 5
  • That's the way I achieved, but the output is not vectorial so texts appears blurred :/ – AlbertUI Feb 09 '22 at 11:50
  • 1
    Hey! So you can use `NSImage(data: host.dataWithPDF(inside: host.bounds))` where `host` is an `NSHostingView` to get vector data. Keep in mind this doesn't work for everything! In my very basic test, the text was carried over, but not the system symbol. – Quinn Feb 09 '22 at 19:12