UI基础 - Quartz2D 03:图形上下文栈 | 绘图路径

图形上下文栈

1 - 先看代码

① 绘制两条线,其中为第一条线设置状态

复制代码
 1 - (void)drawRect:(CGRect)rect{
 2     
 3     // 上下文
 4     CGContextRef ctx = UIGraphicsGetCurrentContext();
 5 
 6     // 第一条线
 7     CGContextMoveToPoint(ctx, 20, 100);
 8     CGContextAddLineToPoint(ctx, 100, 320);
 9     
10     CGContextSetLineWidth(ctx, 12);// 线宽
11     [[UIColor brownColor] set]; // 颜色
12     CGContextSetLineCap(ctx,kCGLineCapRound);// 线条两端的样式为圆角
13     CGContextStrokePath(ctx);  // 渲染
14 
15     // 第二条线
16     CGContextMoveToPoint(ctx, 40, 200);
17     CGContextAddLineToPoint(ctx, 80, 100);
18     CGContextStrokePath(ctx);
19 
20 }
复制代码

运行效果:第二条线也会被渲染成第一条线的状态

② 要让两条线的颜色不一样且第二条线变成默认效果

方式一:将第二条线还原成默认值

复制代码
 1 - (void)drawRect:(CGRect)rect{
 2 
 3     CGContextRef ctx=UIGraphicsGetCurrentContext();
 4     // 第一条线
 5     CGContextMoveToPoint(ctx, 20, 100);
 6     CGContextAddLineToPoint(ctx, 100, 320);
 7     CGContextSetLineWidth(ctx, 12);
 8     [[UIColor brownColor]set];
 9     CGContextSetLineCap(ctx,kCGLineCapRound);
10     CGContextStrokePath(ctx);
11 
12     // 第二条线
13     CGContextMoveToPoint(ctx, 40, 200);
14     CGContextAddLineToPoint(ctx, 80, 100);
15     // 清空状态:就是重设默认状态
16     CGContextSetLineWidth(ctx, 1);
17     [[UIColor blackColor] set];
18     CGContextSetLineCap(ctx,kCGLineCapButt);
19     CGContextStrokePath(ctx);
20 
21 }
复制代码

方式二:将第一条线从开始到渲染完成代码,移动到第二条线渲染结束的代码后面

复制代码
 1 - (void)drawRect:(CGRect)rect{
 2     // 获取上下文
 3     CGContextRef ctx=UIGraphicsGetCurrentContext();
 4     // 第二条线:移动到第一条线前
 5     CGContextMoveToPoint(ctx, 40, 200);
 6     CGContextAddLineToPoint(ctx, 80, 100);
 7     CGContextStrokePath(ctx);
 8 
 9     // 第一条线
10     CGContextMoveToPoint(ctx, 20, 100);
11     CGContextAddLineToPoint(ctx, 100, 320);
12     CGContextSetLineWidth(ctx, 12);
13     [[UIColor brownColor]set];
14     CGContextSetLineCap(ctx,kCGLineCapRound);
15     CGContextStrokePath(ctx);
16 }
复制代码

运行效果:两方式效果等同

其实无论是哪种方式,都算不上解决问题:如果有情况,必须要先画第一条线再画第二条线且要求在交叉部分,第二条线盖在第一条线的上面,那么只能使用第一种做法;如果有新的情况,要求在这个基础上再画两条线,那就需要清空上下文中的状态很多次!为了解决这个问题,下面给大家介绍图形上下文栈

2 - 图形上下文以理解为在上下文中有一块单独的区域用来先绘制图形,当调用渲染方法的时候,才会把绘制好的图形显示到 view 上去,就是说在绘制图形区域时,上下文会保存绘图状态信息:线宽、圆角、颜色等等,其实在渲染完成之前就已经把所要绘制的图形在绘图区画好了

  如果没有对绘图状态进行重新设置,当画完第一条线的时候使用的绘图状态还保存在图形上下文中,难么在第二条线进行渲染之前,就会根据第一条线(上一份绘图状态)对第二条线进行相应的设置,渲染后把第二条线显示到屏幕上,这就是刚开始的代码效果图的原因

3 - 在获取图形上下文之后,通过 CGContextSaveGState(ctx) 方法把当前获取的上下文拷贝一份,保存一份图形上下文。在画第二条线之前使用 CGContextRestoreGState(ctx) 方法还原开始时保存的那份图形上下文

复制代码
 1 - (void)drawRect:(CGRect)rect{
 2     
 3     CGContextRef ctx=UIGraphicsGetCurrentContext();
 4     // 保存一份最初的图形上下文
 5     CGContextSaveGState(ctx);
 6 
 7     //第一条线
 8     CGContextMoveToPoint(ctx, 20, 100);
 9     CGContextAddLineToPoint(ctx, 100, 320);
10     CGContextSetLineWidth(ctx, 12);
11     [[UIColor brownColor]set];
12     CGContextSetLineCap(ctx,kCGLineCapRound);
13     CGContextStrokePath(ctx);
14 
15     // 还原开始时的图形上下文
16     CGContextRestoreGState(ctx);
17     // 第二条线
18     CGContextMoveToPoint(ctx, 40, 200);
19     CGContextAddLineToPoint(ctx, 80, 100);
20     CGContextStrokePath(ctx);
21     
22     // 注:上下文在栈里保存了几次,就取几次
23     // 比如这里保存了 1 次,却取出了 2 次,那么在取第 2 次的时候,因为栈里为空就会直接挂掉
24     // CGContextRestoreGState(ctx);// 超次取出 Crash
25     // CGContextMoveToPoint(ctx, 140, 300);
26     // CGContextAddLineToPoint(ctx, 180, 130);
27     // CGContextStrokePath(ctx);
28     
29 }
复制代码

绘图路径

1 - 在画线的时候,方法的内部默认创建一个 path,它把路径都放到了 path 里面

① 创建路径:CGMutablePathRef 调用该方法相当于创建了一个路径,这个路径用来保存绘图信息

② 把绘图信息添加到路径里:以前的方法是点的位置添加到图形上下文中,默认会在内部创建一个 path 用来保存绘图信息,其实在图形上下文中有一块存储空间专门用来存储绘图信息,这块空间就是 CGMutablePathRef

2 - 代码示例

① 使用 path

复制代码
 1 - (void)drawRect:(CGRect)rect{
 2     
 3     CGContextRef ctx = UIGraphicsGetCurrentContext();
 4 
 5     // 创建一条直线绘图的路径(但凡通过 Quartz2D 中带有 creat、copy、retain 方法创建出来的值都必须要释放)
 6     CGMutablePathRef path = CGPathCreateMutable();
 7     CGPathMoveToPoint(path, NULL, 20, 20);
 8     CGPathAddLineToPoint(path, NULL, 200, 300);
 9     CGContextAddPath(ctx, path);
10     CGContextStrokePath(ctx);
11 
12     // 释放前面创建的路径
13     
14     CGPathRelease(path);// 方式一
15     // CFRelease(path);// 方式二
16 }
复制代码

它和下方代码是等价的,其实下面这种代码的阅读性不怎么友好,不便于区分

1 - (void)drawRect:(CGRect)rect{
2     
3     CGContextRef ctx=UIGraphicsGetCurrentContext();
4     CGContextMoveToPoint(ctx, 20, 20);
5     CGContextAddLineToPoint(ctx, 200, 300);
6     CGContextStrokePath(ctx);
7 }

② 一个 path 就代表一条路径,使用起来清晰明了,如果要在上下文中绘制多个图形,建议使用 path

复制代码
 1 - (void)drawRect:(CGRect)rect{
 2 
 3     CGContextRef ctx = UIGraphicsGetCurrentContext();
 4 
 5     // 直线
 6     CGMutablePathRef path = CGPathCreateMutable();
 7     CGPathMoveToPoint(path, NULL, 20, 20);
 8     CGPathAddLineToPoint(path, NULL, 200, 300);
 9     CGContextAddPath(ctx, path);
10 
11     //
12     CGMutablePathRef path1 = CGPathCreateMutable();
13     CGPathAddEllipseInRect(path1, NULL, CGRectMake(50, 50, 100, 100));
14     CGContextAddPath(ctx, path1);
15     CGContextStrokePath(ctx);
16 
17     // 释放
18     CGPathRelease(path);
19     CFRelease(path1);// 属于更底层的 cocafoundation框架
20 }
复制代码

运行效果

 

 

posted on   低头捡石頭  阅读(32)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示