iOS开发UI篇—Quartz2D使用(图形上下文栈)

简介:

一、qurza2d是怎么将绘图信息和绘图的属性绘制到图形上下文中去的?

说明:

新建一个项目,自定义一个view类和storyboard关联后,重写该类中的drowrect方法。

画线的三个步骤:

(1)获取上下文

(2)绘图

(3)渲染

要求:画两条单独的线

代码和效果图:

复制代码
 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 //第二条线 11 CGContextMoveToPoint(ctx, 40, 200);
12 CGContextAddLineToPoint(ctx, 80, 100);
13 //渲染 14  CGContextStrokePath(ctx);
15 16 }
复制代码

效果图:

设置线段的宽度:两头为圆形,颜色等。

代码和效果图(发现第二条线也被渲染成第一条线的样式和状态)

复制代码
 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 //设置第一条线的状态
11 //设置线条的宽度 12 CGContextSetLineWidth(ctx, 12);
13 //设置线条的颜色 14 [[UIColor brownColor]set];
15 //设置线条两端的样式为圆角 16  CGContextSetLineCap(ctx,kCGLineCapRound);
17 //对线条进行渲染 18  CGContextStrokePath(ctx);
19 20 //第二条线 21 CGContextMoveToPoint(ctx, 40, 200);
22 CGContextAddLineToPoint(ctx, 80, 100);
23 //渲染 24  CGContextStrokePath(ctx);
25 26 }
复制代码

效果图:

新的需求:要让两条线的颜色不一样,要求第二条线变成原版的样子。要达到上面的要求,有以下几种做法:

第一种做法:

在对第二条线进行设置的时候,清空它的状态

复制代码
 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 //设置第一条线的状态
11 //设置线条的宽度 12 CGContextSetLineWidth(ctx, 12);
13 //设置线条的颜色 14 [[UIColor brownColor]set];
15 //设置线条两端的样式为圆角 16  CGContextSetLineCap(ctx,kCGLineCapRound);
17 //对线条进行渲染 18  CGContextStrokePath(ctx);
19 20 //第二条线 21 CGContextMoveToPoint(ctx, 40, 200);
22 CGContextAddLineToPoint(ctx, 80, 100);
23 24 //清空状态 25 CGContextSetLineWidth(ctx, 1);
26 [[UIColor blackColor]set];
27  CGContextSetLineCap(ctx,kCGLineCapButt);
28 29 //渲染 30  CGContextStrokePath(ctx);
31 32 }
复制代码

第二种做法:

把第一条线从开始绘制到渲染的代码剪切到第二条线渲染完成之后,这样先绘制并渲染了第一条线,该线并没有对绘制信息进行过设置,显示出来的第二条线即位系统默认的效果。

复制代码
 1 - (void)drawRect:(CGRect)rect
 2 {
 3 //获取上下文  4 CGContextRef ctx=UIGraphicsGetCurrentContext();
 5 //绘图
 6  7 //第二条线  8 CGContextMoveToPoint(ctx, 40, 200);
 9 CGContextAddLineToPoint(ctx, 80, 100);
10 11 //清空状态
12 // CGContextSetLineWidth(ctx, 1);
13 // [[UIColor blackColor]set];
14 15 // CGContextSetLineCap(ctx,kCGLineCapButt);
16 17 //渲染 18  CGContextStrokePath(ctx);
19 20 //第一条线 21 CGContextMoveToPoint(ctx, 20, 100);
22 CGContextAddLineToPoint(ctx, 100, 320);
23 24 //设置第一条线的状态
25 //设置线条的宽度 26 CGContextSetLineWidth(ctx, 12);
27 //设置线条的颜色 28 [[UIColor brownColor]set];
29 //设置线条两端的样式为圆角 30  CGContextSetLineCap(ctx,kCGLineCapRound);
31 //对线条进行渲染 32  CGContextStrokePath(ctx);
33 }
复制代码

两种方式完成的效果相同:

但是有的情况下,必须要先画第一条线再画第二条线,要求在交叉部分,第二条线盖在第一条线的上面。如果要求是这样,那么只能使用第一种做法,但是如果现在有新的需求,要求在这个基础上再画两条线,那就需要清空ctx中的状态很多次,很麻烦。为了解决这个问题,下面给大家介绍图形上下文栈。

二、绘图的完整过程

程序启动,显示自定义的view。当程序第一次显示在我们眼前的时候,程序会调用drawRect:方法,在里面获取了图形上下文(在内存中拥有了),然后利用图形上下文保存绘图信息,可以理解为图形上下文中有一块区域用来保存绘图信息,有一块区域用来保存绘图的状态(线宽,圆角,颜色)。直线不是直接绘制到view上的,可以理解为在图形上下文中有一块单独的区域用来先绘制图形,当调用渲染方法的时候,再把绘制好的图形显示到view上去。

在绘制图形区域,会去保存绘图状态区域中查找对应的状态信息(线宽,圆角,颜色),然后在绘图区域把对第一条直线绘制完成。其实在渲染之前,就已经把直线在绘制图形区域画好了。

如图:

说明:这些示意图和本文中的程序代码块,不具备一一对应关系,只是为了说明绘图的完整过程。

调用渲染方法的时候,把绘制图形区域已经画好的图形直接显示到view上,就是我们看到的样子了。

如图:

画第二条的时候,如果没有对绘图状态进行重新设置,那么可以发现画第一天线的时候使用的绘图状态还保存在图形上下文中,在第二条线进行渲染之前,会根据第一条线(上一份绘图状态)对第二条线进行相应的设置,渲染后把第二条线显示到屏幕上。

参考代码:

复制代码
 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 //设置第一条线的状态
11 //设置线条的宽度 12 CGContextSetLineWidth(ctx, 12);
13 //设置线条的颜色 14 [[UIColor brownColor]set];
15 //设置线条两端的样式为圆角 16  CGContextSetLineCap(ctx,kCGLineCapRound);
17 //对线条进行渲染 18  CGContextStrokePath(ctx);
19 20 //第二条线 21 CGContextMoveToPoint(ctx, 40, 200);
22 CGContextAddLineToPoint(ctx, 80, 100);
23 //渲染 24  CGContextStrokePath(ctx);
25 }
复制代码

如果清空了状态,则在渲染之前,在绘制图形区域对第二条线进行绘制的时候,会去查找当前的绘图信息(已经更改——清空),根据绘图信息对第二条线进行绘制,调用渲染方法的时候把第二条线显示到view上。

参考代码:

复制代码
 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 //设置第一条线的状态
11 //设置线条的宽度 12 CGContextSetLineWidth(ctx, 12);
13 //设置线条的颜色 14 [[UIColor brownColor]set];
15 //设置线条两端的样式为圆角 16  CGContextSetLineCap(ctx,kCGLineCapRound);
17 //对线条进行渲染 18  CGContextStrokePath(ctx);
19 20 //第二条线 21 CGContextMoveToPoint(ctx, 40, 200);
22 CGContextAddLineToPoint(ctx, 80, 100);
23 24 //清空状态 25 CGContextSetLineWidth(ctx, 1);
26 [[UIColor blackColor]set];
27  CGContextSetLineCap(ctx,kCGLineCapButt);
28 29 //渲染 30  CGContextStrokePath(ctx);
31 }
复制代码

三、图形上下文栈

1.简单说明

在获取图形上下文之后,通过

CGContextSaveGState(ctx);

方法,把当前获取的上下文拷贝一份,保存一份最纯洁的图形上下文。

在画第二条线之前,使用CGContextRestoreGState(ctx);方法,还原开始的时候保存的那份最纯洁的图形上下文。

代码:

复制代码
 1 - (void)drawRect:(CGRect)rect
 2 {
 3 //获取上下文  4 CGContextRef ctx=UIGraphicsGetCurrentContext();
 5 //保存一份最初的图形上下文  6  CGContextSaveGState(ctx);
 7  8 //绘图
 9 //第一条线 10 CGContextMoveToPoint(ctx, 20, 100);
11 CGContextAddLineToPoint(ctx, 100, 320);
12 13 //设置第一条线的状态
14 //设置线条的宽度 15 CGContextSetLineWidth(ctx, 12);
16 //设置线条的颜色 17 [[UIColor brownColor]set];
18 //设置线条两端的样式为圆角 19  CGContextSetLineCap(ctx,kCGLineCapRound);
20 //对线条进行渲染 21  CGContextStrokePath(ctx);
22 23 //还原开始的时候保存的那份最纯洁的图形上下文 24  CGContextRestoreGState(ctx);
25 //第二条线 26 CGContextMoveToPoint(ctx, 40, 200);
27 CGContextAddLineToPoint(ctx, 80, 100);
28 29 //清空状态
30 // CGContextSetLineWidth(ctx, 1);
31 // [[UIColor blackColor]set];
32 // CGContextSetLineCap(ctx,kCGLineCapButt);
33 34 //渲染 35  CGContextStrokePath(ctx);
36 }
复制代码

2.图形上下文栈机制

画第一条线的时候,会把当前的图形上下文拷贝一份保存到图形上下文栈中。

画第二条线的时候,去图形上下文栈中取出栈顶的绘图信息,作为第二条线的状态信息,第二条线的状态信息也是据此(最初保存的那份图形上下文)进行绘制。

注意:在栈里保存了几次,那么就可以取几次(比如不能保存了1次,取两次,在取第二次的时候,栈里为空会直接挂掉)。

目录
相关文章
|
4天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
1月前
|
开发框架 JavaScript 前端开发
鸿蒙NEXT开发声明式UI是咋回事?
【10月更文挑战第15天】鸿蒙NEXT的声明式UI基于ArkTS,提供高效简洁的开发体验。ArkTS扩展了TypeScript,支持声明式UI描述、自定义组件及状态管理。ArkUI框架则提供了丰富的组件、布局计算和动画能力。开发者仅需关注数据变化,UI将自动更新,简化了开发流程。此外,其前后端分层设计与编译时优化确保了高性能运行,利于生态发展。通过组件创建、状态管理和渲染控制等方式,开发者能快速构建高质量的鸿蒙应用。
110 3
|
11天前
|
监控 iOS开发 开发者
iOS性能优化:深入函数调用栈与符号化技术
在iOS开发中,函数调用栈是理解程序执行流程和优化性能的关键。当应用出现性能问题或崩溃时,能够准确地读取和解析调用栈信息对于快速定位问题至关重要。本文将探讨iOS中的函数调用栈,以及如何通过符号化技术进行有效的性能调优。
24 3
|
11天前
|
监控 算法 iOS开发
深入探索iOS函数调用栈:符号化与性能调优实战
在iOS开发中,理解函数调用栈对于性能调优和问题排查至关重要。函数调用栈记录了程序执行过程中的函数调用顺序,通过分析调用栈,我们可以识别性能瓶颈和潜在的代码问题。本文将分享iOS函数调用栈的基本概念、符号化过程以及如何利用调用栈进行性能调优。
29 2
|
21天前
|
开发框架 JavaScript 前端开发
HarmonyOS UI开发:掌握ArkUI(包括Java UI和JS UI)进行界面开发
【10月更文挑战第22天】随着科技发展,操作系统呈现多元化趋势。华为推出的HarmonyOS以其全场景、多设备特性备受关注。本文介绍HarmonyOS的UI开发框架ArkUI,探讨Java UI和JS UI两种开发方式。Java UI适合复杂界面开发,性能较高;JS UI适合快速开发简单界面,跨平台性好。掌握ArkUI可高效打造符合用户需求的界面。
74 8
|
24天前
|
JavaScript API 开发者
掌握ArkTS,打造HarmonyOS应用新视界:从“Hello World”到状态管理,揭秘鸿蒙UI开发的高效秘诀
【10月更文挑战第19天】ArkTS(ArkUI TypeScript)是华为鸿蒙系统中用于开发用户界面的声明式编程语言,结合了TypeScript和HarmonyOS的UI框架。本文介绍ArkTS的基本语法,包括组件结构、模板和脚本部分,并通过“Hello World”和计数器示例展示其使用方法。
51 1
|
1月前
|
缓存 测试技术 C#
使用Radzen Blazor组件库开发的基于ABP框架炫酷UI主题
【10月更文挑战第20天】本文介绍了使用 Radzen Blazor 组件库开发基于 ABP 框架的炫酷 UI 主题的步骤。从准备工作、引入组件库、设计主题、集成到 ABP 框架,再到优化和调试,详细讲解了每个环节的关键点和注意事项。通过这些步骤,你可以打造出高性能、高颜值的应用程序界面。
|
1月前
|
JavaScript 索引
Vue开发中Element UI/Plus使用指南:常见问题(如Missing required prop: “value“)及中文全局组件配置解决方案
Vue开发中Element UI/Plus使用指南:常见问题(如Missing required prop: “value“)及中文全局组件配置解决方案
110 0
|
6月前
|
Android开发 缓存 双11
android的基础ui组件,Android开发社招面试经验
android的基础ui组件,Android开发社招面试经验
android的基础ui组件,Android开发社招面试经验
|
2月前
|
前端开发 开发者 UED
前端只是切图仔?来学学给开发人看的UI设计
该文章针对前端开发者介绍了UI设计的基本原则与实践技巧,覆盖了布局、色彩理论、字体选择等方面的知识,并提供了设计工具和资源推荐,帮助开发者提升产品的视觉与交互体验。