Search code examples
swiftmacoscocoa

output NSView to png


OS version: 14.2
XCode version: 15.2
swift version: 5.9.2
target: macOS application(cocoa)

I want to make NSView created programmatically to png, But bitmapImageRepForCachingDisplay returns nil because bounds is (0.0, 0.0, 0.0, 0.0).

I have tried to change bounds to NSRect(x: 0, y: 0, width: 200, height: 200) but the png was empty.

what is wrong?

class ViewController: NSViewController {
    var myView: MyView?

    override func viewDidLoad() {
        super.viewDidLoad()

        self.myView = MyView()
        self.view.addSubview(self.myView!)
    }

    @IBAction func buttonClicked(_ sender: Any) {
        self.myView?.makeImage()
    }
}
class MyView: NSView {

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        let context = NSGraphicsContext.current!.cgContext
        context.saveGState()

        context.setStrokeColor(NSColor.red.cgColor)
        context.strokeEllipse(in: NSRect(x: 100, y: 100, width: 50, height: 50))

        context.restoreGState()
    }
}

extension NSView {
    func makeImage() -> Void {
        guard let rep = bitmapImageRepForCachingDisplay(in: bounds) else { return }
        rep.size = bounds.size
        cacheDisplay(in: bounds, to: rep)
        guard let pngData = rep.representation(using: .png, properties: [:]) else { return }
        do {
            try pngData.write(to: URL(fileURLWithPath: NSHomeDirectory())
                .appendingPathComponent("/test.png"))
        } catch {
            print(error)
        }
    }
}

Solution

  • The bounds of the view is (0.0, 0.0, 0.0, 0.0) because MyView() creates a view with frame (0.0, 0.0, 0.0, 0.0). The designated initializer of NSView is init(frame:).

    self.myView = MyView(frame: NSRect(x: 0, y: 0, width: 200, height: 200))