I need to take the contents of an NSView
and put them in an NSImage
, for an experimental project. Is this possible? I did some Googling, tried two methods that I found - but they didn't really work. Any suggestions?
How do I take a "screenshot" of an NSView?
Which methods didn't work and why? –
Sibbie
[[NSImage alloc] initWithData:[view dataWithPDFInsideRect:[view bounds]]];
+1 It's worth noting that, although this is the right way in general, there are some cases where it will not work, eg views like
NSOpenGLView
that have their own OpenGL rendering context. In that case you need to get the pixel data directly and create a bitmap rep from it, which is a bit less neat. –
Windbound Obs: shadows and cornerRadius will be ignored –
Hickson
If view is layer backed (wantsLayer = YES) this solution will ignore some layer properties, like backgroundColor, cornerRadius etc. I recommend code from another answer, using cacheDisplayInRect function, it should work in all cases. –
Quiteria
From WWDC 2012 Session 245 (translated to Swift):
let viewToCapture = self.window!.contentView!
let rep = viewToCapture.bitmapImageRepForCachingDisplay(in: viewToCapture.bounds)!
viewToCapture.cacheDisplay(in: viewToCapture.bounds, to: rep)
let img = NSImage(size: viewToCapture.bounds.size)
img.addRepresentation(rep)
swift 3 let rep = viewToCapture.bitmapImageRepForCachingDisplay(in: viewToCapture.bounds)! viewToCapture.cacheDisplay(in: viewToCapture.bounds, to: rep) let img = NSImage(size: viewToCapture.bounds.size) img.addRepresentation(rep) –
Fogel
Note: doesn't always work correctly. I'm having issued with a label that has a rotated coordinate system. –
Thymus
This was the best solution for me, unlike the pdf version, this also accounts for shadows and rounded corners. Unlike the window cap version, this only captures the current view. Obj C code:
- (NSImage *)screenCap { NSBitmapImageRep *bitmap = [self bitmapImageRepForCachingDisplayInRect:self.bounds]; [self cacheDisplayInRect:self.bounds toBitmapImageRep:bitmap]; NSImage *result = [[NSImage alloc] initWithSize:self.bounds.size]; [result addRepresentation:bitmap]; return result; }
–
Hickson I've been using this successfully in 10.14 & 10.15 (codrut's Obj C version above) but for some reason I get no image on 10.13. –
Clarisclarisa
I had the problem that subviews with
visible=NO
were still showing up in the PDF version. Using this version solved it. Still looking for a solution, though, which will also include an AVPlayerView
's current frame. Suggestions welcome :) –
Lomax I'm using an NSBox with .underPageBackgroundColor as the fillColor and this doesn't capture the color properly –
Kepi
[[NSImage alloc] initWithData:[view dataWithPDFInsideRect:[view bounds]]];
+1 It's worth noting that, although this is the right way in general, there are some cases where it will not work, eg views like
NSOpenGLView
that have their own OpenGL rendering context. In that case you need to get the pixel data directly and create a bitmap rep from it, which is a bit less neat. –
Windbound Obs: shadows and cornerRadius will be ignored –
Hickson
If view is layer backed (wantsLayer = YES) this solution will ignore some layer properties, like backgroundColor, cornerRadius etc. I recommend code from another answer, using cacheDisplayInRect function, it should work in all cases. –
Quiteria
let dataOfView = view.dataWithPDFInsideRect(view.bounds)
let imageOfView = NSImage(data: dataOfView)
NSView.bitmapImageRepForCachingDisplay()
(mentioned in this answer) doesn't render the colors correctly on some views.
CGWindowListCreateImage()
works perfectly for me.
Here's my implementation:
extension NSView {
@objc func takeScreenshot() -> NSImage? {
let screenRect = self.rectInQuartzScreenCoordinates()
guard let window = self.window else {
assert(false); return nil
}
let windowID = CGWindowID(window.windowNumber)
guard let screenshot = CGWindowListCreateImage(screenRect, .optionIncludingWindow, windowID, []) else {
assert(false); return nil
}
return NSImage(cgImage: screenshot, size: self.frame.size)
}
}
This code uses a method NSView.rectInQuartzScreenCoordinates()
. To implement it you'll first have to convert the bounds of your view to screenCoordinates using NSView
and NSWindow
methods and then you need to flip the coordinates like this.
© 2022 - 2024 — McMap. All rights reserved.