CGContextSaveGState和UIGraphicsPushContext的区别

这个东东在学习Quart2D时距离现在已经接触两年了吧,但是现在才想起写写他们两者的区别,以前本人有点懒散,只是记录在学习笔记上,不太爱写这些博客,没办法,自己现在也写写这些文文了,免得以后老是找笔记,有点烦人。

- (void)drawRect:(CGRect)rect {
     //获得当前上下文
     CGContextRef ctx=UIGraphicsGetCurrentContext();
     [[UIColor redColor] setStroke];

     CGContextSaveGState(UIGraphicsGetCurrentContext());
 //    UIGraphicsPushContext(ctx);

    //画椭圆
    CGContextAddEllipseInRect(ctx, CGRectMake(200, 130, 60, 30));
   //以下等价
   //CGContextStrokePath(ctx);
    CGContextDrawPath(ctx, kCGPathStroke);

   //画圆形
   CGContextAddEllipseInRect(ctx, CGRectMake(140, 170, 50, 50));
   CGContextSetLineWidth(ctx, 3);
   [[UIColor yellowColor] setStroke];
   CGContextStrokePath(ctx);

   CGContextRestoreGState(UIGraphicsGetCurrentContext());
//    UIGraphicsPopContext();

  //画圆弧
   CGContextAddArc(ctx, 200, 50, 50, M_PI_4, M_PI, 1);
   CGContextStrokePath(ctx);
}

图形上下文中包含一个保存过的图形状态堆栈。

在Quartz创建图形上下文时,该堆栈是空的。CGContextSaveGState函数的作用是将当前图形状态推入堆栈。之后,您对图形状态所做的修改会影响随后的描画操作,但不影响存储在堆栈中的拷贝。在修改完成后,您可以通过CGContextRestoreGState函数把堆栈顶部的状态弹出,返回到之前的图形状态。这种推入和弹出的方式是回到之前图形状态的快速方法,避免逐个撤消所有的状态修改;这也是将某些状态(比如裁剪路径)恢复到原有设置的唯一方式。

这段代码如果用CGContextSaveGState的话椭圆和圆弧的颜色则为红色,而圆形的颜色则为黄色。而使用UIGraphicsPushContext的话椭圆为红色,而圆形和圆弧则为黄色。

使用CGContextSaveGState和CGContextRestoreGState是将上下文入栈和出栈。请不要与UIGraphicsPushContext和UIGraphicsPopContext混淆。它们做的并不是同一件事。CGContextSaveGState是上下文的当前状态,而UIGraphicsPushContext是更改当前上下文。

使用CGContextSaveGState的话,那么在CGContextSaveGState和CGContextRestoreGState内的内容则不会影响CGContextSaveGState之外的代码的上下文设置,在用CGContextRestoreGState出栈时之前入栈前的上下文设置会影响后面的代码。上面上下文在入栈前的颜色为红色,而在入栈期间画圆形时颜色更改为黄色,但是出栈后的颜色画圆弧时依然为入栈前的红色。而使用UIGraphicsPushContext的话,UIGraphicsPushContext内的内容更改会影响UIGraphicsPushContext和UIGraphicsPopContext范围外的代码。上面代码中上下文在入栈前的颜色为红色,而在入栈期间画圆形时颜色更改为黄色,但是出栈后的颜色画圆弧时居然不是入栈前的红色,而为黄色。记住,CGContextSaveGState和CGContextRestoreGState必须成对的出现,UIGraphicsPushContext和UIGraphicsPopContext也是如此。

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:压栈当前的绘制状态

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

定义 CALayer 子类,override func draw(in ctx: CGContext) 的时候,如果我们在该方法中使用使用UIKit做绘制(比如UIColor.set(),UIBezierPath.stroke()),就有必要在方法头使用 UIGraphicsPushContext(ctx),在方法尾使用UIGraphicsPopContext()。因为UIKit的绘制,是基于Graphics stack的top context的,只有在UIGraphicsPushContext(ctx)之后,才有
current context供这些UIKit绘制模块使用。

采用UIKit的截图方式,调用UIView的drawViewHierarchyInRect:afterScreenUpdates:从该view中截取图像.
因为必须调用UIKit中的绘图方法,所以才需要用到UIGraphicsPushContext/UIGraphicsPopContext
类似的UIKit方法还有

[self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; // 将当前layer渲染到image context中

// 绘制贝塞尔曲线
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 100));
[[UIColor blueColor] setFill];
[p fill];

总结
CGContextSaveGState/CGContextRestoreGState用于记录和恢复已存储的绘图context。
CGContextSaveGState是压栈当前的绘制状态,而UIGraphicsPushContext:压栈当前的绘制对象,生成新的绘制图层。对于UIGraphicsPushContext的使用,很多都是与UIKit配合使用.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容

  • Core Graphics Framework是一套基于C的API框架,使用了Quartz作为绘图引擎。它提供了低...
    ShanJiJi阅读 1,523评论 0 20
  • --绘图与滤镜全面解析 概述 在iOS中可以很容易的开发出绚丽的界面效果,一方面得益于成功系统的设计,另一方面得益...
    韩七夏阅读 2,717评论 2 10
  • 你为你的目标到底做了什么? 书翻了几页? 课听了几节? 题做了多少? 考试日期日日逼近, 还要后悔吗? 你为你新一...
    思思有片海阅读 138评论 0 0
  • 任务: 把任意整数 12345 转换成 54321 包括(-12345 ——> -54321)计算过程中如有溢出返...
    KALong阅读 323评论 0 0
  • 在我毫无准备的情况下你像一个天使悄悄而至,尽管最初的我有太多忧愁,太多的不知所措,但是我还是想告诉你:宝贝,我...
    守住一座城阅读 265评论 0 3