AppleScriptObjC: Saving an NSVisualEffectView to PNG

I have an AppleScriptObjC applet that imports an image into an NSImageView, overlays an NSVisualEffectView, and then adds both views to an NSView. This all works fine. My problem arises when attempting to save the NSView to disk as a PNG; using NSBitmapImageRep a PNG is created but it only contains the NSImage with no NSVisualEffectView overlay. I’m assuming that using NSBitmapImageRep is the root of the problem, but despite much Googling I’m stumped as to how to successfully export an NSView that includes an NSVisualEffectView.

This is the handler I’m currently using to save the NSView (named “theContainerView”):

on saveImage()
	theContainerView's lockFocus()
	set theRep to (NSBitmapImageRep's alloc()'s initWithFocusedViewRect:{{0, 0}, {600, 600}})
	theContainerView's unlockFocus()
	set theData to (theRep's representationUsingType:NSPNGFileType |properties|:(missing value))
	set classicPath to (choose file name default location (path to desktop) default name "Saved.png")
	set posixPath to POSIX path of classicPath
	theData's writeToFile:posixPath atomically:true
end saveImage

Any help would be much appreciated.

Given that NSVisualEffectView is for interface effects, it may well be that there’s no obvious way to do what you want.

What kind of effect are you creating that requires an NSVisualEffectView?

Thanks for your reply, Shane. I’m using NSVisualEffectView to overlay a semi-transparent blurred band across an image, with the band containing text that has NSAppearance properties applied to it. Ultimately I want to export the NSView as a bitmap image that I can then use in a design project. Here’s my work-in-progress script:

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSFont : a reference to current application's NSFont
property NSView : a reference to current application's NSView
property NSColor : a reference to current application's NSColor
property NSImage : a reference to current application's NSImage
property NSButton : a reference to current application's NSButton
property NSWindow : a reference to current application's NSWindow
property NSTextView : a reference to current application's NSTextView
property NSImageView : a reference to current application's NSImageView
property NSAppearance : a reference to current application's NSAppearance
property NSPNGFileType : a reference to current application's NSPNGFileType
property NSVisualEffectView : a reference to current application's NSVisualEffectView
property NSBitmapImageRep : a reference to current application's NSBitmapImageRep
property NSFontWeightHeavy : a reference to current application's NSFontWeightHeavy
property NSTextAlignmentCenter : a reference to current application's NSTextAlignmentCenter
property NSBackingStoreBuffered : a reference to current application's NSBackingStoreBuffered

property theContainerView : missing value

# define image
set theImagePath to POSIX path of (path to desktop folder) & "Sunflower.png"
set theImage to (NSImage's alloc()'s initWithContentsOfFile:theImagePath)

# define image view
set theImageView to (NSImageView's alloc()'s initWithFrame:{{0, 0}, {600, 600}})
theImageView's setImage:theImage

# define text view
set theTextView to (NSTextView's alloc's initWithFrame:{{10, 0}, {580, 105}})
theTextView's setFont:(NSFont's systemFontOfSize:80 weight:NSFontWeightHeavy)
theTextView's setAlignment:NSTextAlignmentCenter
theTextView's setTextColor:(NSColor's tertiaryLabelColor)
theTextView's setAppearance:(NSAppearance's vibrantLightAppearance)
theTextView's setAllowsVibrancy:true
theTextView's setBackgroundColor:(NSColor's clearColor)
theTextView's setString:"Sunflower"
theTextView's setEditable:false

# define overlay view & add text view
set theOverlayView to (NSVisualEffectView's alloc()'s initWithFrame:{{0, 250}, {600, 100}})
theOverlayView's setBlendingMode:1
theOverlayView's setMaterial:1 # light material
theOverlayView's setState:1
theOverlayView's addSubview:theTextView

# define container view & add image view & overlay view
set theContainerView to (NSView's alloc()'s initWithFrame:{{0, 38}, {600, 600}})
theContainerView's addSubview:theImageView
theContainerView's addSubview:theOverlayView

# define 'Save Image' button
set theButton to (NSButton's alloc()'s initWithFrame:{{246, 5}, {110, 25}})
theButton's setTitle:"Save Image"
theButton's setBezelStyle:1
theButton's setTarget:me
theButton's setAction:"saveImage"

# define window & add container view & button view
set theWindow to (NSWindow's alloc()'s initWithContentRect:{{0, 0}, {600, 638}} styleMask:3 backing:NSBackingStoreBuffered defer:true)
theWindow's |center|()
theWindow's setTitle:"Sunflower"
theWindow's contentView()'s addSubview:theContainerView
theWindow's contentView()'s addSubview:theButton
theWindow's orderFrontRegardless()
theWindow's makeKeyAndOrderFront:me

on saveImage()
	theContainerView's lockFocus()
	set theRep to (NSBitmapImageRep's alloc()'s initWithFocusedViewRect:{{0, 0}, {600, 600}})
	theContainerView's unlockFocus()
	set theData to (theRep's representationUsingType:NSPNGFileType |properties|:(missing value))
	set classicPath to (choose file name default location (path to desktop) default name "Saved.png")
	set posixPath to POSIX path of classicPath
	theData's writeToFile:posixPath atomically:true
end saveImage

Up to now I’ve been cheating by simply taking a screenshot, but I was wondering/hoping that there was a more legitimate/efficient way of saving the NSView and its contents as an image.

1 Like

I wonder if your problem is that you’re not actually drawing your view, so the effects never get applied to the image.