我有一个自定义NSView'MyView',它显示了创建成本很高的NSImage。理想情况下,此渲染应在发生背景威胁时进行,并且MyView应该在渲染完成后自行更新。
为此,我遵循了WWDC 2013, Session 215 (around 4:00)中的建议。
它的工作原理如下:当调用drawRect并且尚未创建图像时,将在后台队列上触发渲染。在那里,创建了图像,并将其存储在实例变量中,并再次(在主线程上)调用setNeedsDisplay以将视图标记为脏。这将第二次调用drawRect,现在该图像已存在并且可以绘制:
- (void)drawRect:(NSRect)dirtyRect
{
// Do we have an image?
if( self.image )
{
// Yes, we can draw the image (and invalidate it right away for demo purposes)
[self.image drawInRect:self.bounds];
self.image = nil;
}
else
{
// No, we have to async render the image first and mark the view as dirty afterwards
CGSize imageSize = self.bounds.size;
dispatch_async( dispatch_get_global_queue( QOS_CLASS_USER_INTERACTIVE, 0 ), ^
{
self.image = [self _renderImageWithSize:imageSize];
dispatch_async( dispatch_get_main_queue(), ^
{
[self setNeedsDisplayInRect:dirtyRect];
});
});
}
}
- (NSImage *)_renderImageWithSize:(NSSize)size
{
// Simulate expensive image rendering (just for demo purposes)
NSBitmapImageRep * bitmapRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil pixelsWide:size.width pixelsHigh:size.height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace bytesPerRow:0 bitsPerPixel:32];
NSGraphicsContext * context = [NSGraphicsContext graphicsContextWithBitmapImageRep:bitmapRep];
[NSGraphicsContext saveGraphicsState];
[NSGraphicsContext setCurrentContext:context];
// Draw oval with random hue.
float hue = ( (float)( labs( random() % 100 )) / 100.0 );
[[NSColor colorWithHue:hue saturation:0.5 brightness:1.0 alpha:1.0] setFill];
[[NSBezierPath bezierPathWithOvalInRect:NSMakeRect( 0.0, 0.0, size.width, size.height )] fill];
[NSGraphicsContext restoreGraphicsState];
NSImage * image = [[NSImage alloc] init];
[image addRepresentation:bitmapRep];
// Simulate super-expensive rendering
sleep( 1 );
return image;
}
该代码可以正常工作,但是会产生令人讨厌的闪烁。似乎在对drawRect的第一次调用中清除了视图。它保持清除状态,直到第二个drawRect实际绘制了渲染的图像。
当然,我可以绘制旧图像,但我不想不必要地绘制过时的数据。
是否可以防止在drawRect中清除视图?