转至:https://www.jianshu.com/p/be38212c0f79

CoreGraphics与UIKit

这边从iOS绘图教程 提取一些重要的内容。

Core Graphics Framework是一套基于C的API框架,使用了Quartz作为绘图引擎。iOS支持两套图形API族:Core Graphics/QuartZ 2D 和OpenGL ES。

Core Graphics API所有的操作都在一个上下文中进行。所以在绘图之前需要获取该上下文并传入执行渲染的函数中。如果你正在渲染一副在内存中的图片,此时就需要传入图片所属的上下文。获得一个图形上下文是我们完成绘图任务的第一步,你可以将图形上下文理解为一块画布。如果你没有得到这块画布,那么你就无法完成任何绘图操作。当然,有许多方式获得一个图形上下文,这里我介绍两种最为常用的获取方法。

  1. 创建一个图片类型的上下文。调用UIGraphicsBeginImageContextWithOptions函数就可获得用来处理图片的图形上下文。利用该上下文,你就可以在其上进行绘图,并生成图片。调用UIGraphicsGetImageFromCurrentImageContext函数可从当前上下文中获取一个UIImage对象。记住在你所有的绘图操作后别忘了调用UIGraphicsEndImageContext函数关闭图形上下文。

  2. 利用cocoa为你生成的图形上下文。当你子类化了一个UIView并实现了自己的drawRect:方法后,一旦drawRect:方法被调用,Cocoa就会为你创建一个图形上下文,此时你对图形上下文的所有绘图操作都会显示在UIView上。

判断一个上下文是否为当前图形上下文需要注意的几点:

  1. UIGraphicsBeginImageContextWithOptions函数不仅仅是创建了一个适用于图形操作的上下文,并且该上下文也属于当前上下文。
  2. drawRect方法被调用时,UIView的绘图上下文属于当前图形上下文。
  3. 回调方法所持有的context:参数并不会让任何上下文成为当前图形上下文。此参数仅仅是对一个图形上下文的引用罢了。

作为初学者,很容易被UIKit和Core Graphics两个支持绘图的框架迷惑。

UIKit

像UIImage、NSString(绘制文本)、UIBezierPath(绘制形状)、UIColor都知道如何绘制自己。这些类提供了功能有限但使用方便的方法来让我们完成绘图任务。一般情况下,UIKit就是我们所需要的。

使用UiKit,你只能在当前上下文中绘图,所以如果你当前处于UIGraphicsBeginImageContextWithOptions函数或drawRect:方法中,你就可以直接使用UIKit提供的方法进行绘图。如果你持有一个context:参数,那么使用UIKit提供的方法之前,必须将该上下文参数转化为当前上下文。幸运的是,调用UIGraphicsPushContext 函数可以方便的将context:参数转化为当前上下文,记住最后别忘了调用UIGraphicsPopContext函数恢复上下文环境。

Core Graphics

这是一个绘图专用的API族,它经常被称为QuartZ或QuartZ 2D。Core Graphics是iOS上所有绘图功能的基石,包括UIKit。

使用Core Graphics之前需要指定一个用于绘图的图形上下文(CGContextRef),这个图形上下文会在每个绘图函数中都会被用到。如果你持有一个图形上下文context:参数,那么你等同于有了一个图形上下文,这个上下文也许就是你需要用来绘图的那个。如果你当前处于UIGraphicsBeginImageContextWithOptions函数或drawRect:方法中,并没有引用一个上下文。为了使用Core Graphics,你可以调用UIGraphicsGetCurrentContext函数获得当前的图形上下文。

stackoverflow的问题

在stackoverflow上,有这样一个问题CGContextSaveGState vs UIGraphicsPushContext问了两者区别,这里列一下高票答案:

UIGraphicsPushContext(context) pushes context onto a stack of CGContextRefs (making context the current drawing context), whereas CGContextSaveGState(context) pushes the current graphics state onto the stack of graphics states maintained by context. You should use UIGraphicsPushContext if you need to make a new CGContextRef the current drawing context, and you should use CGContextSaveGState when you're working with one graphics context and just want to save, for example: the current transform state, fill or stroke colors, etc.

翻译一下就是:
UIGraphicsPushContext(context)将context压到一个CGContextRefs(使得context成为current context)的栈中。而CGContextSaveGState(context)将当前绘制状态压到一个context维护的绘制状态的栈中。你可以使用UIGraphicsPushContext当你需要在当前的context去创建一个新的CGContextRef,同时你可以使用CGContextSaveGState当你在处理一个绘制context并且只是想保存的它的时候。比如:当前的变换状态,填充或者线条颜色等。

以上答案其实就是在说:

  1. UIGraphicsPushContext:压栈当前的绘制对象,生成新的绘制图层
  2. CGContextSaveGState:压栈当前的绘制状态

实例

CGContextSaveGState

我们这里用一段实际代码:

-(void)drawRect:(CGRect)rect{

    CGContextRef ctx=UIGraphicsGetCurrentContext();
[[UIColor redColor] setStroke]; //红色 CGContextSaveGState(UIGraphicsGetCurrentContext()); CGContextAddEllipseInRect(ctx, CGRectMake(, , , ));
CGContextSetLineWidth(ctx, );
[[UIColor yellowColor] setStroke]; //黄色
CGContextStrokePath(ctx); CGContextRestoreGState(UIGraphicsGetCurrentContext()); CGContextAddEllipseInRect(ctx, CGRectMake(, , , )); //红色
CGContextStrokePath(ctx);
}

运行一下看结果:

可以看到,CGContextSaveGState存储下来了当前红色和默认的线条状态,然后切换颜色到黄色和10粗度的线条画圈,然后在CGContextRestoreGState恢复到了红色和默认的线条状态进行画圈,这个就是存储当前绘制状态的意思。

UIGraphicsPushContext

同样用一段实际代码:

- (void)viewDidLoad {
[super viewDidLoad]; CALayer *layer=[CALayer layer];
layer.bounds=CGRectMake(, , , );
layer.position=CGPointMake(, );
layer.delegate=self;
[layer setNeedsDisplay];
[self.view.layer addSublayer:layer];
} -(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
UIImage *image = [UIImage imageNamed:@"test.jpg"]; UIGraphicsPushContext(ctx);
[image drawInRect:CGRectMake(, , , )];
UIGraphicsPopContext();
}

运行看一下结果:

如果你将UIGraphicsPushContext(ctx);UIGraphicsPopContext();删去的话,是无法进行绘制的。

原因是,UIKit的绘制必须在当前的上下文中绘制,而UIGraphicsPushContext可以将当前的参数context转化为可以UIKit绘制的上下文,进行绘制图片。

总结

CGContextSaveGState是压栈当前的绘制状态,而UIGraphicsPushContext:压栈当前的绘制对象,生成新的绘制图层。对于UIGraphicsPushContext的使用,很多都是与UIKit配合使用,更详细的对于CoreGraphics的介绍,可以参考iOS绘图教程

参考资料

1.CGContextSaveGState vs UIGraphicsPushContext
2.iOS --- CoreGraphics中三种绘图context切换方式的区别
3.iOS core graphic使用分析
4.iOS绘图教程 | iOS绘图教程

CoreGraphics之CGContextSaveGState与UIGraphicsPushContext的更多相关文章

  1. Quartz 2D中CGContextSaveGState与UIGraphicsPushContext

    CGContextRef: An opaque type that represents a Quartz 2D drawing environment. Graphics Context是图形上下文 ...

  2. iOS绘图框架CoreGraphics分析

    由于CoreGraphics框架有太多的API,对于初次接触或者对该框架不是十分了解的人,在绘图时,对API的选择会感到有些迷茫,甚至会觉得iOS的图形绘制有些繁琐.因此,本文主要介绍一下iOS的绘图 ...

  3. iOS中 CoreGraphics快速绘图(详解) 韩俊强的博客

    每日更新关注:http://weibo.com/hanjunqiang  新浪微博 第一步:先科普一下基础知识: Core Graphics是基于C的API,可以用于一切绘图操作 Core Graph ...

  4. iOS-绘图之CoreGraphics框架

    第一步:先科普一下基础知识: Core Graphics是基于C的API,可以用于一切绘图操作 Core Graphics 和Quartz 2D的区别 quartz是一个通用的术语,用于描述在iOS和 ...

  5. Quartz2D 编程指南(四)位图与图像遮罩、CoreGraphics 绘制 Layer

    概览 图形上下文 路径 颜色与颜色空间 变换 图案 阴影 渐变 透明层 Quartz 2D 中的数据管理 位图与图像遮罩 CoreGraphics 绘制 Layer 位图与图像遮罩 简介 位图与图像遮 ...

  6. CoreGraphics QuartzCore CGContextTranslateCTM 用法

      原点是: 左下角 旋转: 逆时针 位移: 右上为正, 左下为负 CGContextTranslateCTM CGContextRotateCTM CGContextScaleCTM 而且, 以上几 ...

  7. 【转】 CoreGraphics QuartzCore CGContextTranslateCTM 用法

    原文:http://blog.csdn.net/sqc3375177/article/details/25708447 CoreGraphics.h 一些常用旋转常量 #define M_E 2.71 ...

  8. CoreGraphics 之CGAffineTransform仿射变换(3)

    CoreGraphics 之CGAffineTransform仿射变换(3)   CoreGraphics 的 仿射变换 可以用于 平移.旋转.缩放变换路径 或者图形上下文. (1)平移变换将路径或图 ...

  9. CoreGraphics QuartzCore CGContextTranslateCTM 说明

    CoreGraphics.h 一些经常使用旋转常量 #define M_E 2.71828182845904523536028747135266250 e 
#define M_LOG2E 1.442 ...

随机推荐

  1. java读写excel文件( POI解析Excel)

    package com.zhx.base.utils; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi ...

  2. Python虚拟环境的安装和配置-virtualenv与windows下多个python版本共存

    Python虚拟环境的安装和配置-virtualenv与windows下多个python版本共存 windows下多个python版本共存 https://www.python.org/downloa ...

  3. 你有所不知的<script>元素

    向html页面中插入javascript的主要方法,就是使用<script>元素. <script>定义了下列6个属性: async:可选.表示应该立即下载脚本,但不应妨碍页面 ...

  4. Java内存管理-Stackoverflow问答-Java是传值还是传引用?(十一)

    勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 本文导图: 一.由一个提问引发的思考 在Stack Overflow 看到这样一个问题 ...

  5. 使用vue iview遇到的一些问题

    使用阿里巴巴图标库 下载代码 这五个文件 iconfount.css 如果导入多个文件记得把@font-face复制到里面 改成./路径 //main.js import './assets/font ...

  6. Unity容器在asp.net mvc中的IOC应用及AOP应用

    <asp.net-mvc框架揭秘>一书中,有个示例,是使用unity容器来注入自定义的控制器工厂.代码示例可以自己去下载源码,在这里我就不说了.IOC容器的本质是解耦的实例化接口类,而如何 ...

  7. SourceTree 文件被锁异常

    公司用的代码管理工具是 Git 客户端用的是 SourceTree ,前些天 SourceTree 发生文件被锁异常,导致文件无法上传,下载,今天特意做个记录 异常: 解决方法:

  8. BZOJ.1535.[POI2005]SZA-Template(KMP DP)

    BZOJ 洛谷 \(Description\) 给定一个字符串\(s\),求一个最短的字符串\(t\)满足,将\(t\)拼接多次后,可以得到\(s\).拼接是指,可以将\(t\)放在当前串的任意位置, ...

  9. linux修改文件为可执行文件

    修改shell为可执行文件 chmod +x test2.sh chmod 751 file   给file的属主分配读.写.执行(7)的权限,给file的所在组分配读.执行(5)的权限,给其他用户分 ...

  10. [BZOJ2863]愤怒的元首

    Description: Pty生活在一个奇葩的国家,这个国家有n个城市,编号为1~n. ​ 每个城市到达其他城市的路径都是有向的. ​ 不存在两个城市可以互相到达. 这个国家的元首现在很愤怒,他大喊 ...