我们可以给每个『线刷』创建一个独立的图层,但是实现起来有很大的问题。屏幕上允许同时出现图层上线数量大约是几百,那样我们很快就会超出的。这种情况下我们没什么办法,就用Core Graphics吧(除非你想用OpenGL做一些更复杂的事情)。

        我们的『黑板』应用的最初实现见清单13.3,我们更改了之前版本的DrawingView,用一个画刷位置的数组代替UIBezierPath。图13.2是运行结果

    清单13.3 简单的类似黑板的应用

    图13.2 用程序绘制一个简单的『素描』

    图13.3

    图13.3 帧率和线条质量会随时间下降。

        为了减少不必要的绘制,Mac OS和iOS设备将会把屏幕区分为需要重绘的区域和不需要重绘的区域。那些需要重绘的部分被称作『脏区域』。在实际应用中,鉴于非矩形区域边界裁剪和混合的复杂性,通常会区分出包含指定视图的矩形位置,而这个位置就是『脏矩形』。

        当一个视图被改动过了,TA可能需要重绘。但是很多情况下,只是这个视图的一部分被改变了,所以重绘整个寄宿图就太浪费了。但是Core Animation通常并不了解你的自定义绘图代码,它也不能自己计算出脏区域的位置。然而,你的确可以提供这些信息。

        当你检测到指定视图或图层的指定部分需要被重绘,你直接调用-setNeedsDisplayInRect:来标记它,然后将影响到的矩形作为参数传入。这样就会在一次视图刷新时调用视图的-drawRect:(或图层代理的-drawLayer:inContext:方法)。

        你应该将你的绘制工作限制在这个矩形中。任何在此区域之外的绘制都将被自动无视,但是这样CPU花在计算和抛弃上的时间就浪费了,实在是太不值得了。

        相比依赖于Core Graphics为你重绘,裁剪出自己的绘制区域可能会让你避免不必要的操作。那就是说,如果你的裁剪逻辑相当复杂,那还是让Core Graphics来代劳吧,记住:当你能高效完成的时候才这样做。

        清单13.4 展示了一个-addBrushStrokeAtPoint:方法的升级版,它只重绘当前线刷的附近区域。另外也会刷新之前线刷的附近区域,我们也可以用CGRectIntersectsRect()来避免重绘任何旧的线刷以不至于覆盖已更新过的区域。这样做会显著地提高绘制效率(见图13.4)

        清单13.4 用-setNeedsDisplayInRect:来减少不必要的绘制

    1. {
    2. //add brush stroke to array
    3. [self.strokes addObject:[NSValue valueWithCGPoint:point]];
    4. //set dirty rect
    5. [self setNeedsDisplayInRect:[self brushRectForPoint:point]];
    6. }
    7. {
    8. return CGRectMake(point.x - BRUSH_SIZE/2, point.y - BRUSH_SIZE/2, BRUSH_SIZE, BRUSH_SIZE);
    9. }
    10. - (void)drawRect:(CGRect)rect
    11. //redraw strokes
    12. for (NSValue *value in self.strokes) {
    13. //get point
    14. //get brush rect
    15. CGRect brushRect = [self brushRectForPoint:point];
    16. //only draw brush stroke if it intersects dirty rect
    17. if (CGRectIntersectsRect(rect, brushRect)) {
    18. //draw brush stroke
    19. [[UIImage imageNamed:@"Chalk.png"] drawInRect:brushRect];
    20. }