iOS開發UI篇—Quartz2D使用(圖形上下文棧)
一、qurza2d是怎麼将繪圖資訊和繪圖的屬性繪制到圖形上下文中去的?
說明:
建立一個項目,自定義一個view類和storyboard關聯後,重寫該類中的drowrect方法。
畫線的三個步驟:
(1)擷取上下文
(2)繪圖
(3)渲染
要求:畫兩條單獨的線
代碼和效果圖:
1 - (void)drawRect:(CGRect)rect
2 {
3 //擷取上下文
4 CGContextRef ctx=UIGraphicsGetCurrentContext();
5 //繪圖
6 //第一條線
7 CGContextMoveToPoint(ctx, 20, 100);
8 CGContextAddLineToPoint(ctx, 100, 320);
9
10 //第二條線
11 CGContextMoveToPoint(ctx, 40, 200);
12 CGContextAddLineToPoint(ctx, 80, 100);
13 //渲染
14 CGContextStrokePath(ctx);
15
16 }
效果圖:

設定線段的寬度:兩頭為圓形,顔色等。
代碼和效果圖(發現第二條線也被渲染成第一條線的樣式和狀态)
1 - (void)drawRect:(CGRect)rect
2 {
3 //擷取上下文
4 CGContextRef ctx=UIGraphicsGetCurrentContext();
5 //繪圖
6 //第一條線
7 CGContextMoveToPoint(ctx, 20, 100);
8 CGContextAddLineToPoint(ctx, 100, 320);
9
10 //設定第一條線的狀态
11 //設定線條的寬度
12 CGContextSetLineWidth(ctx, 12);
13 //設定線條的顔色
14 [[UIColor brownColor]set];
15 //設定線條兩端的樣式為圓角
16 CGContextSetLineCap(ctx,kCGLineCapRound);
17 //對線條進行渲染
18 CGContextStrokePath(ctx);
19
20 //第二條線
21 CGContextMoveToPoint(ctx, 40, 200);
22 CGContextAddLineToPoint(ctx, 80, 100);
23 //渲染
24 CGContextStrokePath(ctx);
25
26 }
新的需求:要讓兩條線的顔色不一樣,要求第二條線變成原版的樣子。要達到上面的要求,有以下幾種做法:
第一種做法:
在對第二條線進行設定的時候,清空它的狀态
1 - (void)drawRect:(CGRect)rect
2 {
3 //擷取上下文
4 CGContextRef ctx=UIGraphicsGetCurrentContext();
5 //繪圖
6 //第一條線
7 CGContextMoveToPoint(ctx, 20, 100);
8 CGContextAddLineToPoint(ctx, 100, 320);
9
10 //設定第一條線的狀态
11 //設定線條的寬度
12 CGContextSetLineWidth(ctx, 12);
13 //設定線條的顔色
14 [[UIColor brownColor]set];
15 //設定線條兩端的樣式為圓角
16 CGContextSetLineCap(ctx,kCGLineCapRound);
17 //對線條進行渲染
18 CGContextStrokePath(ctx);
19
20 //第二條線
21 CGContextMoveToPoint(ctx, 40, 200);
22 CGContextAddLineToPoint(ctx, 80, 100);
23
24 //清空狀态
25 CGContextSetLineWidth(ctx, 1);
26 [[UIColor blackColor]set];
27 CGContextSetLineCap(ctx,kCGLineCapButt);
28
29 //渲染
30 CGContextStrokePath(ctx);
31
32 }
第二種做法:
把第一條線從開始繪制到渲染的代碼剪切到第二條線渲染完成之後,這樣先繪制并渲染了第一條線,該線并沒有對繪制資訊進行過設定,顯示出來的第二條線即位系統預設的效果。
1 - (void)drawRect:(CGRect)rect
2 {
3 //擷取上下文
4 CGContextRef ctx=UIGraphicsGetCurrentContext();
5 //繪圖
6
7 //第二條線
8 CGContextMoveToPoint(ctx, 40, 200);
9 CGContextAddLineToPoint(ctx, 80, 100);
10
11 //清空狀态
12 // CGContextSetLineWidth(ctx, 1);
13 // [[UIColor blackColor]set];
14
15 // CGContextSetLineCap(ctx,kCGLineCapButt);
16
17 //渲染
18 CGContextStrokePath(ctx);
19
20 //第一條線
21 CGContextMoveToPoint(ctx, 20, 100);
22 CGContextAddLineToPoint(ctx, 100, 320);
23
24 //設定第一條線的狀态
25 //設定線條的寬度
26 CGContextSetLineWidth(ctx, 12);
27 //設定線條的顔色
28 [[UIColor brownColor]set];
29 //設定線條兩端的樣式為圓角
30 CGContextSetLineCap(ctx,kCGLineCapRound);
31 //對線條進行渲染
32 CGContextStrokePath(ctx);
33 }
兩種方式完成的效果相同:
但是有的情況下,必須要先畫第一條線再畫第二條線,要求在交叉部分,第二條線蓋在第一條線的上面。如果要求是這樣,那麼隻能使用第一種做法,但是如果現在有新的需求,要求在這個基礎上再畫兩條線,那就需要清空ctx中的狀态很多次,很麻煩。為了解決這個問題,下面給大家介紹圖形上下文棧。
二、繪圖的完整過程
程式啟動,顯示自定義的view。當程式第一次顯示在我們眼前的時候,程式會調用drawRect:方法,在裡面擷取了圖形上下文(在記憶體中擁有了),然後利用圖形上下文儲存繪圖資訊,可以了解為圖形上下文中有一塊區域用來儲存繪圖資訊,有一塊區域用來儲存繪圖的狀态(線寬,圓角,顔色)。直線不是直接繪制到view上的,可以了解為在圖形上下文中有一塊單獨的區域用來先繪制圖形,當調用渲染方法的時候,再把繪制好的圖形顯示到view上去。
在繪制圖形區域,會去儲存繪圖狀态區域中查找對應的狀态資訊(線寬,圓角,顔色),然後在繪圖區域把對第一條直線繪制完成。其實在渲染之前,就已經把直線在繪制圖形區域畫好了。
如圖:
說明:這些示意圖和本文中的程式代碼塊,不具備一一對應關系,隻是為了說明繪圖的完整過程。
調用渲染方法的時候,把繪制圖形區域已經畫好的圖形直接顯示到view上,就是我們看到的樣子了。
畫第二條的時候,如果沒有對繪圖狀态進行重新設定,那麼可以發現畫第一天線的時候使用的繪圖狀态還儲存在圖形上下文中,在第二條線進行渲染之前,會根據第一條線(上一份繪圖狀态)對第二條線進行相應的設定,渲染後把第二條線顯示到螢幕上。
參考代碼:
1 - (void)drawRect:(CGRect)rect
2 {
3 //擷取上下文
4 CGContextRef ctx=UIGraphicsGetCurrentContext();
5 //繪圖
6 //第一條線
7 CGContextMoveToPoint(ctx, 20, 100);
8 CGContextAddLineToPoint(ctx, 100, 320);
9
10 //設定第一條線的狀态
11 //設定線條的寬度
12 CGContextSetLineWidth(ctx, 12);
13 //設定線條的顔色
14 [[UIColor brownColor]set];
15 //設定線條兩端的樣式為圓角
16 CGContextSetLineCap(ctx,kCGLineCapRound);
17 //對線條進行渲染
18 CGContextStrokePath(ctx);
19
20 //第二條線
21 CGContextMoveToPoint(ctx, 40, 200);
22 CGContextAddLineToPoint(ctx, 80, 100);
23 //渲染
24 CGContextStrokePath(ctx);
25 }
如果清空了狀态,則在渲染之前,在繪制圖形區域對第二條線進行繪制的時候,會去查找目前的繪圖資訊(已經更改——清空),根據繪圖資訊對第二條線進行繪制,調用渲染方法的時候把第二條線顯示到view上。
1 - (void)drawRect:(CGRect)rect
2 {
3 //擷取上下文
4 CGContextRef ctx=UIGraphicsGetCurrentContext();
5 //繪圖
6 //第一條線
7 CGContextMoveToPoint(ctx, 20, 100);
8 CGContextAddLineToPoint(ctx, 100, 320);
9
10 //設定第一條線的狀态
11 //設定線條的寬度
12 CGContextSetLineWidth(ctx, 12);
13 //設定線條的顔色
14 [[UIColor brownColor]set];
15 //設定線條兩端的樣式為圓角
16 CGContextSetLineCap(ctx,kCGLineCapRound);
17 //對線條進行渲染
18 CGContextStrokePath(ctx);
19
20 //第二條線
21 CGContextMoveToPoint(ctx, 40, 200);
22 CGContextAddLineToPoint(ctx, 80, 100);
23
24 //清空狀态
25 CGContextSetLineWidth(ctx, 1);
26 [[UIColor blackColor]set];
27 CGContextSetLineCap(ctx,kCGLineCapButt);
28
29 //渲染
30 CGContextStrokePath(ctx);
31 }
三、圖形上下文棧
1.簡單說明
在擷取圖形上下文之後,通過
CGContextSaveGState(ctx);
方法,把目前擷取的上下文拷貝一份,儲存一份最純潔的圖形上下文。
在畫第二條線之前,使用CGContextRestoreGState(ctx);方法,還原開始的時候儲存的那份最純潔的圖形上下文。
代碼:
1 - (void)drawRect:(CGRect)rect
2 {
3 //擷取上下文
4 CGContextRef ctx=UIGraphicsGetCurrentContext();
5 //儲存一份最初的圖形上下文
6 CGContextSaveGState(ctx);
7
8 //繪圖
9 //第一條線
10 CGContextMoveToPoint(ctx, 20, 100);
11 CGContextAddLineToPoint(ctx, 100, 320);
12
13 //設定第一條線的狀态
14 //設定線條的寬度
15 CGContextSetLineWidth(ctx, 12);
16 //設定線條的顔色
17 [[UIColor brownColor]set];
18 //設定線條兩端的樣式為圓角
19 CGContextSetLineCap(ctx,kCGLineCapRound);
20 //對線條進行渲染
21 CGContextStrokePath(ctx);
22
23 //還原開始的時候儲存的那份最純潔的圖形上下文
24 CGContextRestoreGState(ctx);
25 //第二條線
26 CGContextMoveToPoint(ctx, 40, 200);
27 CGContextAddLineToPoint(ctx, 80, 100);
28
29 //清空狀态
30 // CGContextSetLineWidth(ctx, 1);
31 // [[UIColor blackColor]set];
32 // CGContextSetLineCap(ctx,kCGLineCapButt);
33
34 //渲染
35 CGContextStrokePath(ctx);
36 }
2.圖形上下文棧機制
畫第一條線的時候,會把目前的圖形上下文拷貝一份儲存到圖形上下文棧中。
畫第二條線的時候,去圖形上下文棧中取出棧頂的繪圖資訊,作為第二條線的狀态資訊,第二條線的狀态資訊也是據此(最初儲存的那份圖形上下文)進行繪制。
注意:在棧裡儲存了幾次,那麼就可以取幾次(比如不能儲存了1次,取兩次,在取第二次的時候,棧裡為空會直接挂掉)。