UIView: 如何进行非破坏性绘制?

18

我的原始问题:

我正在创建一个简单的绘图应用程序,需要能够在drawRect上绘制现有的、之前绘制过的内容,而不是完全替换它。如何正确地在现有内容上绘制,而不是完全替换它?

根据从这里和其他地方收到的答案,以下是解决方法。

  1. 每次调用drawRect时,都应该准备好重新绘制整个矩形。

  2. 无法通过执行以下操作来防止内容被擦除:

    [self setClearsContextBeforeDrawing: NO];

    这只是给图形引擎的提示,告诉它没有必要为你预清除视图,因为你很可能需要重新绘制整个区域。 它可以防止自动擦除你的视图,但你不能依赖它。

  3. 要在视图顶部绘制而不擦除,请将绘图操作绘制到离屏位图上下文中(系统永远不会清除该上下文)。 然后在drawRect中,从此离屏缓冲区复制到视图中。

示例:

- (id) initWithCoder: (NSCoder*) coder {    
     if (self = [super initWithCoder: coder]) {
         self.backgroundColor = [UIColor clearColor];
         CGSize size = self.frame.size;
         drawingContext = [self createDrawingBufferContext: size];
     }

     return self;
 }

- (CGContextRef) createOffscreenContext: (CGSize) size  {
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, size.width*4, colorSpace, kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colorSpace);

    CGContextTranslateCTM(context, 0, size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    return context;
}


- (void)drawRect:(CGRect) rect {    
    UIGraphicsPushContext(drawingContext);
    CGImageRef cgImage = CGBitmapContextCreateImage(drawingContext); 
    UIImage *uiImage = [[UIImage alloc] initWithCGImage:cgImage];
    UIGraphicsPopContext();
    CGImageRelease(cgImage);
    [uiImage drawInRect: rect];
    [uiImage release];
 }

待办事项: 有人可以优化drawRect使得只使用(通常很小的)修改矩形区域来进行复制吗?

4个回答

5

在IT技术中,通常会将所有内容绘制在离屏图像中,然后在绘制屏幕时仅显示该图像。您可以参考创建位图图形上下文了解更多信息。


哦,那个参考资料看起来是C语言代码。当我尝试将它添加到我的Objective-C类中时,模拟器会崩溃(很遗憾还有调试器一起崩溃了)。 - George Armhold
据我所知,这是Quartz,在iPhone上得到支持。你可能做错了什么...能否向我们展示一下你做了什么? - Martin Cote
当然。我的drawRect()调用了引用示例的“国旗绘制”代码。我已经将其复制/粘贴到:http://pastebin.com/m68230dcd感谢您的时间。 - George Armhold
我觉得你误解了我的意思。你应该创建一个单层上下文,在其中使用Quartz函数渲染你的基本图形。从我所看到的情况来看,你为每个形状都创建了一个新的上下文。 - Martin Cote
我已经解决了,谢谢。从UIView -> UIImage -> UIView复制仍然有点慢。我怀疑如果我能找出如何仅复制实际更改的(小)子矩形而不是复制整个视图,我就可以加快速度。谢谢。 - George Armhold

1

关于优化drawRect方法

尝试这个:

- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGImageRef cgImage = CGBitmapContextCreateImage(drawingContext);
    CGContextClipToRect(context, rect);
    CGContextDrawImage(context, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), cgImage);
    CGImageRelease(cgImage);
}

接下来,您还应该在代码中对这些行进行注释:

//CGContextTranslateCTM(context, 0, size.height);
//CGContextScaleCTM(context, 1.0, -1.0);

同时创建了另一个问题,以确保这是最优解。


0

这可以防止在 drawRect 完成之前擦除您的视图:

[self.layer setNeedsDisplay];

还有,我发现最好在drawRect方法中执行所有绘图(除非你有充分的理由不这样做)。离屏绘制和传输需要更多的时间并增加更多的复杂性,相比之下,一次性绘制所有内容更简单。


0

这似乎是比我之前使用的更好的方法。也就是说,在触摸事件中,我复制即将更新的视图。然后在drawRect中,我将该图像绘制到视图上,并同时进行其他视图更改。

但这似乎效率低下,而我找到的唯一方法。


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