這個東東在學習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配合使用.