Cocoa: 如何将视图渲染成图像?

9

我想知道有没有一种方法可以将视图渲染成图片?就像截屏一样,但我可以指定任何视图给该方法。 我知道如何在iOS中做到这一点。 保存视图内容,但我该如何在Mac OS中实现呢?

3个回答

16

有几种方法可以尝试,但是没有一种方法能够完美解决所有问题。很多问题都与层支持视图相关。

如果您没有层支持或托管视图,则可以使用以下代码:

    NSData* data = [mainView dataWithPDFInsideRect:[mainView bounds]];
    NSImage *img = [[NSImage alloc] initWithData:data];

如果你使用基于图层的视图:

在10.8之前,我认为最好的方法是:

        NSSize mySize = mapImage.bounds.size;
        NSSize imgSize = NSMakeSize( mySize.width, mySize.height );
        NSBitmapImageRep *bir = [mapImage bitmapImageRepForCachingDisplayInRect:[mapImage bounds]];
        [bir setSize:imgSize];
        [mapImage cacheDisplayInRect:[mapImage bounds] toBitmapImageRep:bir];
        caheImg = [[NSImage alloc]initWithSize:imgSize];
        [caheImg addRepresentation:bir];

但在10.8中,bitmapImageRepForCachingDisplayInRect在使用Layer Backed视图时停止工作。

有一个选项可以对屏幕进行截图并裁剪您的视图:

+ (NSImage*) screenCacheImageForView:(NSView*)aView
{
    NSRect originRect = [aView convertRect:[aView bounds] toView:[[aView window] contentView]];

    NSRect rect = originRect;
    rect.origin.y = 0;
    rect.origin.x += [aView window].frame.origin.x;
    rect.origin.y += [[aView window] screen].frame.size.height - [aView window].frame.origin.y - [aView window].frame.size.height;
    rect.origin.y += [aView window].frame.size.height - originRect.origin.y - originRect.size.height;

    CGImageRef cgimg = CGWindowListCreateImage(rect,
                                           kCGWindowListOptionIncludingWindow,
                                           (CGWindowID)[[aView window] windowNumber],
                                           kCGWindowImageDefault);
    return [[NSImage alloc] initWithCGImage:cgimg size:[aView bounds].size];
}

你的版本在位图支持视图中运行良好,并且我已将其添加到 10.8 上,升级了几天到 10.8.3。但是 screenCacheImageForView 在我的视图中引起问题(该视图顶部有 2 个附加的 NSTextField 标签,错误地包含在视图的截图中)。但值得注意的是,在 10.8.3 上,bitmapImageRepForCachingDisplayInRect 可以正常工作 - 在 10.8.3 之前不能这样吗? - konran
也许苹果已经在10.8.3中修复了这个问题,当时我写代码的时候只有10.8.1版本。但是你的应用用户可能仍然使用旧的操作系统版本,所以最好不要在10.8中使用这种方法。 - Remizorrr
苹果公司的10.9版本发布说明声称已经修复了cacheDisplayInRect和支持图层的视图的问题。但是请注意,在某些情况下,我发现这个问题在10.10版本中仍然存在。不过,dataWithPDFInsideRect似乎可以很好地处理支持图层的视图,并生成分辨率独立的图像,这些图像更小,因此现在可能是更好的解决方案。 - Peter N Lewis
我正在运行10.10.5。dataWithPDFInsideRect给了我以下错误:Assertion failed: (s->stack->next != NULL), function CGGStackRestore, file Context/CGGStack.c, line 77。但是bitmapImageRepForCachingDisplayInRect却正常工作。 - Bob Ueland

3

@Remizorr的回答在ObjectiveC下运行很好,尤其是在macOS High Sierra 10.13.4和Xcode 9.2下。它同样适用于使用了layerBacked视图的情况(wantsLayer = true)。以下是更新后的Swift版本:

extension NSView {

func snapshotImage() -> NSImage? {

    guard let window = window,
        let screen = window.screen,
        let contentView = window.contentView else { return nil }

    let originRect = self.convert(self.bounds, to:contentView)
    var rect = originRect
    rect.origin.x += window.frame.origin.x
    rect.origin.y = 0
    rect.origin.y += screen.frame.size.height - window.frame.origin.y - window.frame.size.height
    rect.origin.y += window.frame.size.height - originRect.origin.y - originRect.size.height
    guard window.windowNumber > 0 else { return nil }
    guard let cgImage = CGWindowListCreateImage(rect, .optionIncludingWindow, CGWindowID(window.windowNumber), CGWindowImageOption.bestResolution) else { return nil }

    return NSImage(cgImage: cgImage, size: self.bounds.size)
}

}

注意:即使是在WKWebView上也可以使用此方法,并且它可以获取正在播放的WKWebView视频的快照(而WKWebViews的takeSnapshot无法实现);).


1
谢谢!我简直不敢相信我花了几个小时才找到这个! - SouthernYankee65

2

我在我的应用程序中使用这段代码,它很好地工作。

然而,如果我将应用程序拖到辅助显示器上,屏幕截图就会失效,我捕获的矩形也会变得错误。

以下代码解决了这个问题(我还从计算中删除了一些多余的数学计算,其中一个字段被减去然后再加回来),以防将来有人遇到这个问题:

NSRect originRect = [aView convertRect:[aView bounds] toView:[[aView window] contentView]];

NSArray *screens = [NSScreen screens];
NSScreen *primaryScreen = [screens objectAtIndex:0];

NSRect rect = originRect;
rect.origin.y = 0;
rect.origin.x += [aView window].frame.origin.x;
rect.origin.y = primaryScreen.frame.size.height - [aView window].frame.origin.y - originRect.origin.y - originRect.size.height;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接