Search code examples
macosprintingdarkmode

Build off-screen view from view hierarchy


The apple doc states:

Printing Views

When you print an NSView through an NSPrintOperation, its appearance now gets temporarily replaced by the aqua appearance during rendering. This is done to avoid printing with an inherited dark appearance. The NSView instance’s own appearance property—if it’s set—is left unaltered, so it remains possible to print views with a nonstandard appearance, if desired.

Use separate, off-screen views to print the contents of on-screen windows. To avoid altering the contents of on-screen windows, the darkAqua appearance isn’t replaced when printing views that are simultaneously hosted in a window.

I have a fairly complex NSView hierarchy, which is shown on screen in a window, and I need to print. So it seems I am in this exact case. (and indeed, I see colour problems when printing in dark mode, when all is fine in light mode).

How do I replicate my NSView hierarchy in an offscreen NSView ?


Solution

  • I ended up doing something like this:

    public protocol Printable {
        
        func printView() -> NSView
    }
    
    
    extension NSView: Printable {
        
        @objc
        open func printView() -> NSView {
            let view = NSView(frame: self.frame)
            for subView in self.subviews {
                let printableSubView = subView.printView()
                view.addSubview(printableSubView)
            }
            return view
        }
    }
    

    So each view is responsible for returning a printView of itself that is printable. For example, when I have a view that shows an image, it would just return an NSImageView (with the right image, of the right size etc...).

    Some views can just return an empty NSView if they don't need to be printed (e.g. screen decoration views). That's what the default extension implemntation does.

    And at document level, I have something loke this:

        override func printOperation(withSettings printSettings: [NSPrintInfo.AttributeKey : Any]) throws -> NSPrintOperation {
            let printView   = _myView.printView()
            let printInfo   = NSPrintInfo(dictionary: printSettings)
            printInfo.horizontalPagination  = .automatic
            printInfo.verticalPagination    = .automatic
            return NSPrintOperation(view: printView, printInfo: printInfo)
        }
    

    Works like a charm, whatever the Appearance mode.