在 Cocoa 中将视图层次结构绘制到特定上下文中

Drawing a view hierarchy into a specific context in Cocoa

提问人:Andrew Grant 提问时间:8/19/2008 最后编辑:CœurAndrew Grant 更新时间:3/18/2017 访问量:3171

问:

对于我的应用程序的一部分,我需要创建某个视图及其所有子视图的图像。

为此,我正在创建一个上下文,该上下文包装与视图大小相同的位图,但我不确定如何将视图层次结构绘制到其中。我只需设置上下文并显式调用 drawRect 即可绘制单个视图,但这并不能处理所有子视图。

我在 NSView 界面中看不到任何可以帮助解决这个问题的内容,所以我怀疑解决方案可能位于更高级别。

Objective-C Cocoa macOS

评论


答:

2赞 Chris Hanson 8/19/2008 #1

可以使用 -[NSView dataWithPDFInsideRect:] 将发送到的视图的整个层次结构呈现为 PDF,并作为对象返回。然后,您可以对其进行任何操作,包括将其渲染为位图。NSData

但是,您确定需要位图表示形式吗?毕竟,该 PDF 可以(至少在理论上)与分辨率无关。

1赞 Chris Hanson 8/19/2008 #2

在将焦点锁定在视图上后,可以使用 -[NSBitmapImageRep initWithFocusedViewRect:],使视图本身(及其子视图)呈现到给定的矩形中。

评论

0赞 Jason 9/23/2015
如果视图位于 NSScrollView 中,则其副作用是显示视图的内容,这些内容被裁剪为滚动视图中可见的内容,并包含任何滚动条。
2赞 charles 9/16/2008 #3

我发现自己编写绘图代码是最好的方法:

  • 处理潜在的透明度问题(其他一些选项确实为整个图像添加了白色背景)
  • 性能要好得多

下面的代码并不完美,因为它在从边界到帧时没有处理缩放问题,但它确实考虑了 isFlipped 状态,并且非常适合我使用它的目的。请注意,它只绘制子视图(和子子视图,...递归),但是让它也绘制自己非常容易,只需在实现中添加 a 即可。[self drawRect:[self bounds]]imageWithSubviews

- (void)drawSubviews
{
    BOOL flipped = [self isFlipped];

    for ( NSView *subview in [self subviews] ) {

        // changes the coordinate system so that the local coordinates of the subview (bounds) become the coordinates of the superview (frame)
        // the transform assumes bounds and frame have the same size, and bounds origin is (0,0)
        // handling of 'isFlipped' also probably unreliable
        NSAffineTransform *transform = [NSAffineTransform transform];
        if ( flipped ) {
            [transform translateXBy:subview.frame.origin.x yBy:NSMaxY(subview.frame)];
            [transform scaleXBy:+1.0 yBy:-1.0];
        } else
            [transform translateXBy:subview.frame.origin.x yBy:subview.frame.origin.y];
        [transform concat];

        // recursively draw the subview and sub-subviews
        [subview drawRect:[subview bounds]];
        [subview drawSubviews];

        // reset the transform to get back a clean graphic contexts for the rest of the drawing
        [transform invert];
        [transform concat];
    }
}

- (NSImage *)imageWithSubviews
{
    NSImage *image = [[[NSImage alloc] initWithSize:[self bounds].size] autorelease];
    [image lockFocus];
    // it seems NSImage cannot use flipped coordinates the way NSView does (the method 'setFlipped:' does not seem to help)
    // Use instead an NSAffineTransform
    if ( [self isFlipped] ) {
        NSAffineTransform *transform = [NSAffineTransform transform];
        [transform translateXBy:0 yBy:NSMaxY(self.bounds)];
        [transform scaleXBy:+1.0 yBy:-1.0];
        [transform concat];
    }
    [self drawSubviews];
    [image unlockFocus];
    return image;
}
1赞 wompf 9/16/2008 #4

您要执行的操作已明确可用。请参阅 10.4 AppKit 发行说明中的“NSView 绘图重定向 API”部分。

创建一个用于缓存的 NSBitmapImageRep 并清除它:

NSGraphicsContext *bitmapGraphicsContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:cacheBitmapImageRep];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:bitmapGraphicsContext];
[[NSColor clearColor] set];
NSRectFill(NSMakeRect(0, 0, [cacheBitmapImageRep size].width, [cacheBitmapImageRep size].height));
[NSGraphicsContext restoreGraphicsState];

缓存到它:

-[NSView cacheDisplayInRect:toBitmapImageRep:]

如果要更普遍地绘制到指定的上下文中,正确地处理视图递归和透明度,

-[NSView displayRectIgnoringOpacity:inContext:]

评论

0赞 Matthieu Cormier 2/26/2010
10.4 developer.apple.com/mac/library/releasenotes/Cocoa/ 中的“NSView 绘图重定向 API”链接