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 }
运行效果
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)