iOS开发中使用Quartz2D绘制上下文栈和矩阵的方法_IOS

上下文栈

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

说明:

新建一个项目,自定义一个view类和storyboard关联后,重写该类中的drowrect方法。
画线的三个步骤:
(1)获取上下文
(2)绘图
(3)渲染
要求:画两条单独的线
代码和效果图:

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //获取上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //绘图
    //第一条线
    CGContextMoveToPoint(ctx, 20, 100);
    CGContextAddLineToPoint(ctx, 100, 320);
   
    //第二条线
    CGContextMoveToPoint(ctx, 40, 200);
    CGContextAddLineToPoint(ctx, 80, 100);
    //渲染
    CGContextStrokePath(ctx);
   
}

效果图:

设置线段的宽度:两头为圆形,颜色等。
代码和效果图(发现第二条线也被渲染成第一条线的样式和状态)

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //获取上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //绘图
    //第一条线
    CGContextMoveToPoint(ctx, 20, 100);
    CGContextAddLineToPoint(ctx, 100, 320);
   
    //设置第一条线的状态
    //设置线条的宽度
    CGContextSetLineWidth(ctx, 12);
    //设置线条的颜色
    [[UIColor brownColor]set];
    //设置线条两端的样式为圆角
    CGContextSetLineCap(ctx,kCGLineCapRound);
    //对线条进行渲染
    CGContextStrokePath(ctx);
   
    //第二条线
    CGContextMoveToPoint(ctx, 40, 200);
    CGContextAddLineToPoint(ctx, 80, 100);
    //渲染
    CGContextStrokePath(ctx);
   
}

效果图:

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

第一种做法:
在对第二条线进行设置的时候,清空它的状态

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //获取上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //绘图
    //第一条线
    CGContextMoveToPoint(ctx, 20, 100);
    CGContextAddLineToPoint(ctx, 100, 320);
   
    //设置第一条线的状态
    //设置线条的宽度
    CGContextSetLineWidth(ctx, 12);
    //设置线条的颜色
    [[UIColor brownColor]set];
    //设置线条两端的样式为圆角
    CGContextSetLineCap(ctx,kCGLineCapRound);
    //对线条进行渲染
    CGContextStrokePath(ctx);
   
    //第二条线
    CGContextMoveToPoint(ctx, 40, 200);
    CGContextAddLineToPoint(ctx, 80, 100);
   
    //清空状态
    CGContextSetLineWidth(ctx, 1);
    [[UIColor blackColor]set];
    CGContextSetLineCap(ctx,kCGLineCapButt);
   
    //渲染
    CGContextStrokePath(ctx);
   
}

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

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //获取上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //绘图
   
    //第二条线
    CGContextMoveToPoint(ctx, 40, 200);
    CGContextAddLineToPoint(ctx, 80, 100);
   
    //清空状态
    //    CGContextSetLineWidth(ctx, 1);
    //    [[UIColor blackColor]set];
   
    //    CGContextSetLineCap(ctx,kCGLineCapButt);
   
    //渲染
    CGContextStrokePath(ctx);
   
    //第一条线
    CGContextMoveToPoint(ctx, 20, 100);
    CGContextAddLineToPoint(ctx, 100, 320);
   
    //设置第一条线的状态
    //设置线条的宽度
    CGContextSetLineWidth(ctx, 12);
    //设置线条的颜色
    [[UIColor brownColor]set];
    //设置线条两端的样式为圆角
    CGContextSetLineCap(ctx,kCGLineCapRound);
    //对线条进行渲染
    CGContextStrokePath(ctx);
}

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

但是有的情况下,必须要先画第一条线再画第二条线,要求在交叉部分,第二条线盖在第一条线的上面。如果要求是这样,那么只能使用第一种做法,但是如果现在有新的需求,要求在这个基础上再画两条线,那就需要清空ctx中的状态很多次,很麻烦。为了解决这个问题,下面给大家介绍图形上下文栈。
 
二、绘图的完整过程
程序启动,显示自定义的view。当程序第一次显示在我们眼前的时候,程序会调用drawRect:方法,在里面获取了图形上下文(在内存中拥有了),然后利用图形上下文保存绘图信息,可以理解为图形上下文中有一块区域用来保存绘图信息,有一块区域用来保存绘图的状态(线宽,圆角,颜色)。直线不是直接绘制到view上的,可以理解为在图形上下文中有一块单独的区域用来先绘制图形,当调用渲染方法的时候,再把绘制好的图形显示到view上去。
 
在绘制图形区域,会去保存绘图状态区域中查找对应的状态信息(线宽,圆角,颜色),然后在绘图区域把对第一条直线绘制完成。其实在渲染之前,就已经把直线在绘制图形区域画好了。
如图:

说明:这些示意图和本文中的程序代码块,不具备一一对应关系,只是为了说明绘图的完整过程。
调用渲染方法的时候,把绘制图形区域已经画好的图形直接显示到view上,就是我们看到的样子了。
如图:

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

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //获取上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //绘图
    //第一条线
    CGContextMoveToPoint(ctx, 20, 100);
    CGContextAddLineToPoint(ctx, 100, 320);
   
    //设置第一条线的状态
    //设置线条的宽度
    CGContextSetLineWidth(ctx, 12);
    //设置线条的颜色
    [[UIColor brownColor]set];
    //设置线条两端的样式为圆角
    CGContextSetLineCap(ctx,kCGLineCapRound);
    //对线条进行渲染
    CGContextStrokePath(ctx);
   
    //第二条线
    CGContextMoveToPoint(ctx, 40, 200);
    CGContextAddLineToPoint(ctx, 80, 100);
    //渲染
    CGContextStrokePath(ctx);
}

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

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //获取上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //绘图
    //第一条线
    CGContextMoveToPoint(ctx, 20, 100);
    CGContextAddLineToPoint(ctx, 100, 320);
   
    //设置第一条线的状态
    //设置线条的宽度
    CGContextSetLineWidth(ctx, 12);
    //设置线条的颜色
    [[UIColor brownColor]set];
    //设置线条两端的样式为圆角
    CGContextSetLineCap(ctx,kCGLineCapRound);
    //对线条进行渲染
    CGContextStrokePath(ctx);
   
    //第二条线
    CGContextMoveToPoint(ctx, 40, 200);
    CGContextAddLineToPoint(ctx, 80, 100);
   
    //清空状态
    CGContextSetLineWidth(ctx, 1);
    [[UIColor blackColor]set];
    CGContextSetLineCap(ctx,kCGLineCapButt);
   
    //渲染
    CGContextStrokePath(ctx);
}

三、图形上下文栈
1.简单说明
在获取图形上下文之后,通过 CGContextSaveGState(ctx); 方法,把当前获取的上下文拷贝一份,保存一份最纯洁的图形上下文。
在画第二条线之前,使用CGContextRestoreGState(ctx);方法,还原开始的时候保存的那份最纯洁的图形上下文。
代码:

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //获取上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //保存一份最初的图形上下文
    CGContextSaveGState(ctx);
   
    //绘图
    //第一条线
    CGContextMoveToPoint(ctx, 20, 100);
    CGContextAddLineToPoint(ctx, 100, 320);
   
    //设置第一条线的状态
    //设置线条的宽度
    CGContextSetLineWidth(ctx, 12);
    //设置线条的颜色
    [[UIColor brownColor]set];
    //设置线条两端的样式为圆角
    CGContextSetLineCap(ctx,kCGLineCapRound);
    //对线条进行渲染
    CGContextStrokePath(ctx);
   
    //还原开始的时候保存的那份最纯洁的图形上下文
    CGContextRestoreGState(ctx);
    //第二条线
    CGContextMoveToPoint(ctx, 40, 200);
    CGContextAddLineToPoint(ctx, 80, 100);
   
    //清空状态
//    CGContextSetLineWidth(ctx, 1);
//    [[UIColor blackColor]set];
//    CGContextSetLineCap(ctx,kCGLineCapButt);
   
    //渲染
    CGContextStrokePath(ctx);
}

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

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

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

矩阵操作
一、关于矩阵操作
1.画一个四边形
通过设置两个端点(长和宽)来完成一个四边形的绘制。
代码:

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //画四边形
    //获取图形上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //绘图
    CGContextAddRect(ctx, CGRectMake(20, 50, 100, 100));
    //渲染
    CGContextStrokePath(ctx);
}

说明:通过这种方式画矩形有弱点:画出来的矩形永远都是正的。如下图:

2.画一个歪的四边形
如何画一个歪的矩形?(通过矩阵操作来完成,和形变操作相似)
可以通过矩阵操作,把画出来的东西进行形变(旋转,缩放,平移)
方法:CGContextRotateCTM(<#CGContextRef c#>, <#CGFloat angle#>)该接受两个参数(图形上下文,弧度)
注意点:设置矩阵操作必须要在添加图形之前,如果设置在添加图形之后的话,此时它已经画完了,无效。
代码:

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //画四边形
    //获取图形上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //矩阵操作
    //注意点:设置矩阵操作必须要在添加绘图信息之前
    //旋转45度
    CGContextRotateCTM(ctx, M_PI_4);
   
    //绘图
    CGContextAddRect(ctx, CGRectMake(150, 100, 100, 100));
    //渲染
    CGContextStrokePath(ctx);
}

效果:

二、关于旋转
1.旋转演示
view之所以能够显示视图,是因为它的上面有layer,将来图形也是渲染到layer上面。
且,旋转的时候是整个layer都旋转了,可以再画一个圆进行验证。
代码1(未旋转):

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //获取图形上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //矩阵操作
    //注意点:设置矩阵操作必须要在添加绘图信息之前
    //旋转45度
    //    CGContextRotateCTM(ctx, M_PI_4);
   
    //绘图
    //画四边形
    CGContextAddRect(ctx, CGRectMake(150, 100, 100, 100));
    //画一个圆
    CGContextAddEllipseInRect(ctx, CGRectMake(200, 200, 50, 50));
    //渲染
    CGContextStrokePath(ctx);
}

 效果:

代码2(旋转):

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //获取图形上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //矩阵操作
    //注意点:设置矩阵操作必须要在添加绘图信息之前
    //旋转45度
    CGContextRotateCTM(ctx, M_PI_4);
   
    //绘图
    //画四边形
    CGContextAddRect(ctx, CGRectMake(150, 100, 100, 100));
    //画一个圆
    CGContextAddEllipseInRect(ctx, CGRectMake(200, 200, 50, 50));
    //渲染
    CGContextStrokePath(ctx);
}

效果:

2.关于旋转的补充说明
提示:旋转的时候,是整个layer都旋转了。

三、缩放
方法:CGContextScaleCTM(<#CGContextRef c#>, <#CGFloat sx#>, <#CGFloat sy#>)
该方法接收三个参数(图形上下文,x方向的缩放比例,y方向上的缩放比例
代码示例:

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //获取图形上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //矩阵操作
    //注意点:设置矩阵操作必须要在添加绘图信息之前
    //缩放,x方向缩放0.5倍,y方向缩放1.5倍
    CGContextScaleCTM(ctx, 0.5, 1.5);
   
    //绘图
    //画四边形
    CGContextAddRect(ctx, CGRectMake(150, 100, 100, 100));
    //画一个圆
    CGContextAddEllipseInRect(ctx, CGRectMake(200, 200, 50, 50));
    //渲染
    CGContextStrokePath(ctx);
}

效果:

四、平移
方法: CGContextTranslateCTM(<#CGContextRef c#>, <#CGFloat tx#>, <#CGFloat ty#>)
该方法接收三个参数(图形上下文,x方向的偏移量,y方向上的偏移量)  
代码示例:

复制代码 代码如下:

- (void)drawRect:(CGRect)rect
{
    //获取图形上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //矩阵操作
    //注意点:设置矩阵操作必须要在添加绘图信息之前
    //平移,x方向移动50,y方向移动100
    CGContextTranslateCTM(ctx, 50, 100);
   
    //绘图
    //画四边形
    CGContextAddRect(ctx, CGRectMake(150, 100, 100, 100));
    //画一个圆
    CGContextAddEllipseInRect(ctx, CGRectMake(200, 200, 50, 50));
    //渲染
    CGContextStrokePath(ctx);
}

效果:

提示:坐标原点为view的左上角。

时间: 2024-11-02 08:22:01

iOS开发中使用Quartz2D绘制上下文栈和矩阵的方法_IOS的相关文章

iOS开发中CAlayer层的属性以及自定义层的方法_IOS

CAlayer层的属性一.position和anchorPoint 1.简单介绍 CALayer有2个非常重要的属性:position和anchorPoint @property CGPoint position; 用来设置CALayer在父层中的位置 以父层的左上角为原点(0, 0) @property CGPoint anchorPoint; 称为"定位点"."锚点" 决定着CALayer身上的哪个点会在position属性所指的位置 以自己的左上角为原点(0,

iOS开发中Subview的事件响应以及获取subview的方法_IOS

Subview的事件响应 在view的层级里面,默认情况下subview是可以显示到其父view的frame区域以外的,通过设置clipToBounds属性为YES,可以限制subview的显示区域.但是touch在各个UIView中传递的时候,区域时限制在view的frame内,此处包含两个信息:1.在当前view的frame以外所做的操作是不会传递到该view中的,这一点很容易理解.2.如果touch事件是发生在当前view的frame以外,该view所有的subview将也不会再收到该消息

iOS开发中常见的项目文件与MVC结构优化思路解析_IOS

常见的项目文件介绍 一.项目文件结构示意图 二.文件介绍 1.products文件夹:主要用于mac电脑开发的可执行文件,ios开发用不到这个文件 2.frameworks文件夹主要用来放依赖的框架 3.test文件夹是用来做单元测试的 4.常用的文件夹(项目名称文件夹) (1)XXXinfo.plist文件(在该项目中为  01-常见文件-Info.plist) 1)简单说明 是配置文件,该文件对工程做一些运行期的配置,非常重要,不能删除. 在旧版本xcode创建的工程中,这个配置文件的名字就

详解iOS开发中app的归档以及偏好设置的存储方式_IOS

ios应用数据存储方式(归档) 一.简单说明 在使用plist进行数据存储和读取,只适用于系统自带的一些常用类型才能用,且必须先获取路径相对麻烦: 偏好设置(将所有的东西都保存在同一个文件夹下面,且主要用于存储应用的设置信息) 归档:因为前两者都有一个致命的缺陷,只能存储常用的类型.归档可以实现把自定义的对象存放在文件中.二.代码示例 1.文件结构 2.代码示例  YYViewController.m文件 复制代码 代码如下: // //  YYViewController.m //  02-归

详解iOS开发中UItableview控件的数据刷新功能的实现_IOS

实现UItableview控件数据刷新一.项目文件结构和plist文件 二.实现效果 1.说明:这是一个英雄展示界面,点击选中行,可以修改改行英雄的名称(完成数据刷新的操作). 运行界面: 点击选中行: 修改数据后自动刷新: 三.代码示例 数据模型部分: YYheros.h文件 复制代码 代码如下: // //  YYheros.h //  10-英雄展示(数据刷新) // //  Created by apple on 14-5-29. //  Copyright (c) 2014年 itca

iOS开发中使用SQL语句操作数据库的基本用法指南_IOS

SQL代码应用示例 一.使用代码的方式批量添加(导入)数据到数据库中 1.执行SQL语句在数据库中添加一条信息 插入一条数据的sql语句: 点击run执行语句之后,刷新数据 2.在ios项目中使用代码批量添加多行数据示例 代码示例: 复制代码 代码如下: // //  main.m //  01-为数据库添加多行数据 // //  Created by apple on 14-7-26. //  Copyright (c) 2014年 wendingding. All rights reserv

iOS开发中使用UIDynamic来捕捉动画组件的重力行为_IOS

UIDynamic基本认识 一.简单介绍 1.什么是UIDynamic UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架 可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象 如:重力.弹性碰撞等现象   2.物理引擎的价值 广泛用于游戏开发,经典成功案例是"愤怒的小鸟" 让开发人员可以在远离物理学公式的情况下,实现炫酷的物理仿真效果 提高了游戏开发效率,产生更多优秀好玩的物理仿真游戏   3.知名的2D物理引擎 Box2d Chipmunk   二.使用

iOS开发中对文件目录的访问及管理的基本方法小结_IOS

文件目录的访问 最简单的:(由于是沙盒关系,没有文件夹概念的) 复制代码 代码如下: UIImage* image = [UIImage imageNamed:@"11.png"]; 这个已经是相对app里面打包好的路径,不需要额外添加路径. 还有一种就是需要指定文件路径的: 需要用到NSBundle. 复制代码 代码如下: [[NSBundle mainBundle] resourcePath],这个就是程序的打包后的路径. 如果需要指定路径,就要这样写: 你也可自己拼接: 复制代码

iOS开发中实现一个简单的图片浏览器的实例讲解_IOS

一.程序实现要求 1.要求 2. 界面分析 (1) 需要读取或修改属性的控件需要设置属性 序号标签 图片 图片描述 左边按钮 右边按钮 (2) 需要监听响应事件的对象,需要添加监听方法 左边按钮 右边按钮 二.实现基本功能的程序 复制代码 代码如下: // //  YYViewController.m //  03-图片浏览器初步 // //  Created by apple on 14-5-21. //  Copyright (c) 2014年 itcase. All rights rese