(四十八)Quartz2D引擎进阶
图形上下文栈:
应用,修改过上下文后,下一次画会在这个基础上进行,如果清空状态,需要上下文栈。
可以先把原来的上下文保存起来,然后恢复:
- (void)drawRect:(CGRect)rect { CGContextRef ref = UIGraphicsGetCurrentContext(); // 保存上下文 CGContextSaveGState(ref); CGContextMoveToPoint(ref, 100, 100); CGContextAddLineToPoint(ref, 200, 200); [[UIColor redColor] set]; CGContextSetLineCap(ref, kCGLineCapRound); CGContextSetLineWidth(ref, 5); CGContextStrokePath(ref); // 恢复上下文 CGContextRestoreGState(ref); CGContextMoveToPoint(ref, 250, 200); CGContextAddLineToPoint(ref, 100, 180); CGContextStrokePath(ref); }
在CGContextRef中有一个专门的空间用于保存上下文的状态,在其内部也有一个区域会把要画的东西先画一遍。
CGContextSaveGState是将ctx拷贝一份,放到栈中,Restore则是出栈。
Tip:CorePlot已经封装好了各种函数图。
矩阵操作:
先设置状态,再画才有作用:注意角度转弧度的时候除以180.0。
第一个是改变缩放比例,第二个是跳转转角。
CGContextScaleCTM(ctx, x_scale, y_scale); CGContextRotateCTM(ctx, angle / 180.0 * M_PI_2);
图片的裁剪:
先在图像后面画一个形状,然后调用裁剪方法,最后再显示图片,就可以生成裁剪后的图片:
CGContextRef ctx = UIGraphicsGetCurrentContext(); CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 50, 50)); CGContextClip(ctx); CGContextFillPath(ctx); UIImage *image = [UIImage imageNamed:@"head"]; [image drawInRect:CGRectMake(0, 0, 50, 50)];
注意是先画一个圆,然后在圆心处放一个与圆的外接正方形重合的图片,这样就可以实现圆型裁剪。
注意先添加图形,再执行裁剪,再显示图形,最后显示图片。
刷帧的方法:多次调用绘图函数
注意drawRect只能由系统调用,手动调用无法获得上下文。
调用view的setNeedsDisplay可以让系统调用。
Tip:如果视图没有改变,调用这个方法仍然会重绘。
Tip:在drawRect方法中调用setNeedsDisplay不会引起死循环。
Tip:从storyboard或者xib初始化完成后会调用awakeFromNib,可以在这里做一些初始化的效果,例如定时器。
Tip:1s以上的使用NSTimer,对于高速刷帧,应该使用CADisplayLink方法。
[CADisplayLink displayLinkWithTarget: ... selector: ... ],用link指针指向它。
添加到消息循环的方法:[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSdefaultRunLoop];
上下文显示到iOS手机屏幕,是通过Layer方式显示的。
Window指的是显示在Mac电脑屏幕上。
通过UIKit也可以调用Quarz2D:不需要主动传上下文。
UIRectFill方法可以画矩形,并且不需要传上下文。
通过创建路径的方式来绘图(涉及到内存管理):
实际上,直接使用ctx也是自动创建一个path,下面的过程通过path来绘图:即使是ARC,也需要使用release。
CGContextRef ctx = UIGraphicsGetCurrentContext(); // 1.创建路径 CGMutablePathRef path = CGPathCreateMutable(); // 2.拼接路径 CGPathMoveToPoint(path, NULL, 0, 0); CGPathAddLineToPoint(path, NULL, 100, 100); // 3.添加路径 CGContextAddPath(ctx, path); // 4.渲染 CGContextStrokePath(ctx); // 5.释放path CGPathRelease(path);
有一个统一的方法,CFRelease,可以释放任何对象。