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配合使用.

你可能感兴趣的:(CGContextSaveGState和UIGraphicsPushContext的区别)