iOS開發(fā)中使用Quartz2D繪制上下文棧和矩陣的方法
上下文棧
一、qurza2d是怎么將繪圖信息和繪圖的屬性繪制到圖形上下文中去的?
說明:
新建一個(gè)項(xiàng)目,自定義一個(gè)view類和storyboard關(guān)聯(lián)后,重寫該類中的drowrect方法。
畫線的三個(gè)步驟:
(1)獲取上下文
(2)繪圖
(3)渲染
要求:畫兩條單獨(dú)的線
代碼和效果圖:
- (void)drawRect:(CGRect)rect
{
//獲取上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//繪圖
//第一條線
CGContextMoveToPoint(ctx, 20, 100);
CGContextAddLineToPoint(ctx, 100, 320);
//第二條線
CGContextMoveToPoint(ctx, 40, 200);
CGContextAddLineToPoint(ctx, 80, 100);
//渲染
CGContextStrokePath(ctx);
}
效果圖:
設(shè)置線段的寬度:兩頭為圓形,顏色等。
代碼和效果圖(發(fā)現(xiàn)第二條線也被渲染成第一條線的樣式和狀態(tài))
- (void)drawRect:(CGRect)rect
{
//獲取上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//繪圖
//第一條線
CGContextMoveToPoint(ctx, 20, 100);
CGContextAddLineToPoint(ctx, 100, 320);
//設(shè)置第一條線的狀態(tài)
//設(shè)置線條的寬度
CGContextSetLineWidth(ctx, 12);
//設(shè)置線條的顏色
[[UIColor brownColor]set];
//設(shè)置線條兩端的樣式為圓角
CGContextSetLineCap(ctx,kCGLineCapRound);
//對線條進(jìn)行渲染
CGContextStrokePath(ctx);
//第二條線
CGContextMoveToPoint(ctx, 40, 200);
CGContextAddLineToPoint(ctx, 80, 100);
//渲染
CGContextStrokePath(ctx);
}
效果圖:
新的需求:要讓兩條線的顏色不一樣,要求第二條線變成原版的樣子。要達(dá)到上面的要求,有以下幾種做法:
第一種做法:
在對第二條線進(jìn)行設(shè)置的時(shí)候,清空它的狀態(tài)
- (void)drawRect:(CGRect)rect
{
//獲取上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//繪圖
//第一條線
CGContextMoveToPoint(ctx, 20, 100);
CGContextAddLineToPoint(ctx, 100, 320);
//設(shè)置第一條線的狀態(tài)
//設(shè)置線條的寬度
CGContextSetLineWidth(ctx, 12);
//設(shè)置線條的顏色
[[UIColor brownColor]set];
//設(shè)置線條兩端的樣式為圓角
CGContextSetLineCap(ctx,kCGLineCapRound);
//對線條進(jìn)行渲染
CGContextStrokePath(ctx);
//第二條線
CGContextMoveToPoint(ctx, 40, 200);
CGContextAddLineToPoint(ctx, 80, 100);
//清空狀態(tài)
CGContextSetLineWidth(ctx, 1);
[[UIColor blackColor]set];
CGContextSetLineCap(ctx,kCGLineCapButt);
//渲染
CGContextStrokePath(ctx);
}
第二種做法:
把第一條線從開始繪制到渲染的代碼剪切到第二條線渲染完成之后,這樣先繪制并渲染了第一條線,該線并沒有對繪制信息進(jìn)行過設(shè)置,顯示出來的第二條線即位系統(tǒng)默認(rèn)的效果。
- (void)drawRect:(CGRect)rect
{
//獲取上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//繪圖
//第二條線
CGContextMoveToPoint(ctx, 40, 200);
CGContextAddLineToPoint(ctx, 80, 100);
//清空狀態(tài)
// CGContextSetLineWidth(ctx, 1);
// [[UIColor blackColor]set];
// CGContextSetLineCap(ctx,kCGLineCapButt);
//渲染
CGContextStrokePath(ctx);
//第一條線
CGContextMoveToPoint(ctx, 20, 100);
CGContextAddLineToPoint(ctx, 100, 320);
//設(shè)置第一條線的狀態(tài)
//設(shè)置線條的寬度
CGContextSetLineWidth(ctx, 12);
//設(shè)置線條的顏色
[[UIColor brownColor]set];
//設(shè)置線條兩端的樣式為圓角
CGContextSetLineCap(ctx,kCGLineCapRound);
//對線條進(jìn)行渲染
CGContextStrokePath(ctx);
}
兩種方式完成的效果相同:
但是有的情況下,必須要先畫第一條線再畫第二條線,要求在交叉部分,第二條線蓋在第一條線的上面。如果要求是這樣,那么只能使用第一種做法,但是如果現(xiàn)在有新的需求,要求在這個(gè)基礎(chǔ)上再畫兩條線,那就需要清空ctx中的狀態(tài)很多次,很麻煩。為了解決這個(gè)問題,下面給大家介紹圖形上下文棧。
二、繪圖的完整過程
程序啟動(dòng),顯示自定義的view。當(dāng)程序第一次顯示在我們眼前的時(shí)候,程序會(huì)調(diào)用drawRect:方法,在里面獲取了圖形上下文(在內(nèi)存中擁有了),然后利用圖形上下文保存繪圖信息,可以理解為圖形上下文中有一塊區(qū)域用來保存繪圖信息,有一塊區(qū)域用來保存繪圖的狀態(tài)(線寬,圓角,顏色)。直線不是直接繪制到view上的,可以理解為在圖形上下文中有一塊單獨(dú)的區(qū)域用來先繪制圖形,當(dāng)調(diào)用渲染方法的時(shí)候,再把繪制好的圖形顯示到view上去。
在繪制圖形區(qū)域,會(huì)去保存繪圖狀態(tài)區(qū)域中查找對應(yīng)的狀態(tài)信息(線寬,圓角,顏色),然后在繪圖區(qū)域把對第一條直線繪制完成。其實(shí)在渲染之前,就已經(jīng)把直線在繪制圖形區(qū)域畫好了。
如圖:
說明:這些示意圖和本文中的程序代碼塊,不具備一一對應(yīng)關(guān)系,只是為了說明繪圖的完整過程。
調(diào)用渲染方法的時(shí)候,把繪制圖形區(qū)域已經(jīng)畫好的圖形直接顯示到view上,就是我們看到的樣子了。
如圖:
畫第二條的時(shí)候,如果沒有對繪圖狀態(tài)進(jìn)行重新設(shè)置,那么可以發(fā)現(xiàn)畫第一天線的時(shí)候使用的繪圖狀態(tài)還保存在圖形上下文中,在第二條線進(jìn)行渲染之前,會(huì)根據(jù)第一條線(上一份繪圖狀態(tài))對第二條線進(jìn)行相應(yīng)的設(shè)置,渲染后把第二條線顯示到屏幕上。
參考代碼:
- (void)drawRect:(CGRect)rect
{
//獲取上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//繪圖
//第一條線
CGContextMoveToPoint(ctx, 20, 100);
CGContextAddLineToPoint(ctx, 100, 320);
//設(shè)置第一條線的狀態(tài)
//設(shè)置線條的寬度
CGContextSetLineWidth(ctx, 12);
//設(shè)置線條的顏色
[[UIColor brownColor]set];
//設(shè)置線條兩端的樣式為圓角
CGContextSetLineCap(ctx,kCGLineCapRound);
//對線條進(jìn)行渲染
CGContextStrokePath(ctx);
//第二條線
CGContextMoveToPoint(ctx, 40, 200);
CGContextAddLineToPoint(ctx, 80, 100);
//渲染
CGContextStrokePath(ctx);
}
如果清空了狀態(tài),則在渲染之前,在繪制圖形區(qū)域?qū)Φ诙l線進(jìn)行繪制的時(shí)候,會(huì)去查找當(dāng)前的繪圖信息(已經(jīng)更改——清空),根據(jù)繪圖信息對第二條線進(jìn)行繪制,調(diào)用渲染方法的時(shí)候把第二條線顯示到view上。
參考代碼:
- (void)drawRect:(CGRect)rect
{
//獲取上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//繪圖
//第一條線
CGContextMoveToPoint(ctx, 20, 100);
CGContextAddLineToPoint(ctx, 100, 320);
//設(shè)置第一條線的狀態(tài)
//設(shè)置線條的寬度
CGContextSetLineWidth(ctx, 12);
//設(shè)置線條的顏色
[[UIColor brownColor]set];
//設(shè)置線條兩端的樣式為圓角
CGContextSetLineCap(ctx,kCGLineCapRound);
//對線條進(jìn)行渲染
CGContextStrokePath(ctx);
//第二條線
CGContextMoveToPoint(ctx, 40, 200);
CGContextAddLineToPoint(ctx, 80, 100);
//清空狀態(tài)
CGContextSetLineWidth(ctx, 1);
[[UIColor blackColor]set];
CGContextSetLineCap(ctx,kCGLineCapButt);
//渲染
CGContextStrokePath(ctx);
}
三、圖形上下文棧
1.簡單說明
在獲取圖形上下文之后,通過 CGContextSaveGState(ctx); 方法,把當(dāng)前獲取的上下文拷貝一份,保存一份最純潔的圖形上下文。
在畫第二條線之前,使用CGContextRestoreGState(ctx);方法,還原開始的時(shí)候保存的那份最純潔的圖形上下文。
代碼:
- (void)drawRect:(CGRect)rect
{
//獲取上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//保存一份最初的圖形上下文
CGContextSaveGState(ctx);
//繪圖
//第一條線
CGContextMoveToPoint(ctx, 20, 100);
CGContextAddLineToPoint(ctx, 100, 320);
//設(shè)置第一條線的狀態(tài)
//設(shè)置線條的寬度
CGContextSetLineWidth(ctx, 12);
//設(shè)置線條的顏色
[[UIColor brownColor]set];
//設(shè)置線條兩端的樣式為圓角
CGContextSetLineCap(ctx,kCGLineCapRound);
//對線條進(jìn)行渲染
CGContextStrokePath(ctx);
//還原開始的時(shí)候保存的那份最純潔的圖形上下文
CGContextRestoreGState(ctx);
//第二條線
CGContextMoveToPoint(ctx, 40, 200);
CGContextAddLineToPoint(ctx, 80, 100);
//清空狀態(tài)
// CGContextSetLineWidth(ctx, 1);
// [[UIColor blackColor]set];
// CGContextSetLineCap(ctx,kCGLineCapButt);
//渲染
CGContextStrokePath(ctx);
}
2.圖形上下文棧機(jī)制
畫第一條線的時(shí)候,會(huì)把當(dāng)前的圖形上下文拷貝一份保存到圖形上下文棧中。
畫第二條線的時(shí)候,去圖形上下文棧中取出棧頂?shù)睦L圖信息,作為第二條線的狀態(tài)信息,第二條線的狀態(tài)信息也是據(jù)此(最初保存的那份圖形上下文)進(jìn)行繪制。
注意:在棧里保存了幾次,那么就可以取幾次(比如不能保存了1次,取兩次,在取第二次的時(shí)候,棧里為空會(huì)直接掛掉)。
矩陣操作
一、關(guān)于矩陣操作
1.畫一個(gè)四邊形
通過設(shè)置兩個(gè)端點(diǎn)(長和寬)來完成一個(gè)四邊形的繪制。
代碼:
- (void)drawRect:(CGRect)rect
{
//畫四邊形
//獲取圖形上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//繪圖
CGContextAddRect(ctx, CGRectMake(20, 50, 100, 100));
//渲染
CGContextStrokePath(ctx);
}
說明:通過這種方式畫矩形有弱點(diǎn):畫出來的矩形永遠(yuǎn)都是正的。如下圖:
2.畫一個(gè)歪的四邊形
如何畫一個(gè)歪的矩形?(通過矩陣操作來完成,和形變操作相似)
可以通過矩陣操作,把畫出來的東西進(jìn)行形變(旋轉(zhuǎn),縮放,平移)
方法:CGContextRotateCTM(<#CGContextRef c#>, <#CGFloat angle#>)該接受兩個(gè)參數(shù)(圖形上下文,弧度)
注意點(diǎn):設(shè)置矩陣操作必須要在添加圖形之前,如果設(shè)置在添加圖形之后的話,此時(shí)它已經(jīng)畫完了,無效。
代碼:
- (void)drawRect:(CGRect)rect
{
//畫四邊形
//獲取圖形上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//矩陣操作
//注意點(diǎn):設(shè)置矩陣操作必須要在添加繪圖信息之前
//旋轉(zhuǎn)45度
CGContextRotateCTM(ctx, M_PI_4);
//繪圖
CGContextAddRect(ctx, CGRectMake(150, 100, 100, 100));
//渲染
CGContextStrokePath(ctx);
}
效果:
二、關(guān)于旋轉(zhuǎn)
1.旋轉(zhuǎn)演示
view之所以能夠顯示視圖,是因?yàn)樗纳厦嬗衛(wèi)ayer,將來圖形也是渲染到layer上面。
且,旋轉(zhuǎn)的時(shí)候是整個(gè)layer都旋轉(zhuǎn)了,可以再畫一個(gè)圓進(jìn)行驗(yàn)證。
代碼1(未旋轉(zhuǎn)):
- (void)drawRect:(CGRect)rect
{
//獲取圖形上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//矩陣操作
//注意點(diǎn):設(shè)置矩陣操作必須要在添加繪圖信息之前
//旋轉(zhuǎn)45度
// CGContextRotateCTM(ctx, M_PI_4);
//繪圖
//畫四邊形
CGContextAddRect(ctx, CGRectMake(150, 100, 100, 100));
//畫一個(gè)圓
CGContextAddEllipseInRect(ctx, CGRectMake(200, 200, 50, 50));
//渲染
CGContextStrokePath(ctx);
}
效果:
代碼2(旋轉(zhuǎn)):
- (void)drawRect:(CGRect)rect
{
//獲取圖形上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//矩陣操作
//注意點(diǎn):設(shè)置矩陣操作必須要在添加繪圖信息之前
//旋轉(zhuǎn)45度
CGContextRotateCTM(ctx, M_PI_4);
//繪圖
//畫四邊形
CGContextAddRect(ctx, CGRectMake(150, 100, 100, 100));
//畫一個(gè)圓
CGContextAddEllipseInRect(ctx, CGRectMake(200, 200, 50, 50));
//渲染
CGContextStrokePath(ctx);
}
效果:
2.關(guān)于旋轉(zhuǎn)的補(bǔ)充說明
提示:旋轉(zhuǎn)的時(shí)候,是整個(gè)layer都旋轉(zhuǎn)了。
三、縮放
方法:CGContextScaleCTM(<#CGContextRef c#>, <#CGFloat sx#>, <#CGFloat sy#>)
該方法接收三個(gè)參數(shù)(圖形上下文,x方向的縮放比例,y方向上的縮放比例
代碼示例:
- (void)drawRect:(CGRect)rect
{
//獲取圖形上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//矩陣操作
//注意點(diǎn):設(shè)置矩陣操作必須要在添加繪圖信息之前
//縮放,x方向縮放0.5倍,y方向縮放1.5倍
CGContextScaleCTM(ctx, 0.5, 1.5);
//繪圖
//畫四邊形
CGContextAddRect(ctx, CGRectMake(150, 100, 100, 100));
//畫一個(gè)圓
CGContextAddEllipseInRect(ctx, CGRectMake(200, 200, 50, 50));
//渲染
CGContextStrokePath(ctx);
}
效果:
四、平移
方法: CGContextTranslateCTM(<#CGContextRef c#>, <#CGFloat tx#>, <#CGFloat ty#>)
該方法接收三個(gè)參數(shù)(圖形上下文,x方向的偏移量,y方向上的偏移量)
代碼示例:
- (void)drawRect:(CGRect)rect
{
//獲取圖形上下文
CGContextRef ctx=UIGraphicsGetCurrentContext();
//矩陣操作
//注意點(diǎn):設(shè)置矩陣操作必須要在添加繪圖信息之前
//平移,x方向移動(dòng)50,y方向移動(dòng)100
CGContextTranslateCTM(ctx, 50, 100);
//繪圖
//畫四邊形
CGContextAddRect(ctx, CGRectMake(150, 100, 100, 100));
//畫一個(gè)圓
CGContextAddEllipseInRect(ctx, CGRectMake(200, 200, 50, 50));
//渲染
CGContextStrokePath(ctx);
}
效果:
提示:坐標(biāo)原點(diǎn)為view的左上角。
相關(guān)文章
IOS 數(shù)據(jù)存儲(chǔ)詳解及實(shí)例代碼
這篇文章主要介紹了IOS 數(shù)據(jù)存儲(chǔ)詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02iOS實(shí)現(xiàn)選項(xiàng)卡效果的方法
選項(xiàng)卡在我們?nèi)粘i_發(fā)的時(shí)候經(jīng)常要用到,所以這篇文章給大家分享一種iOS實(shí)現(xiàn)的簡單選項(xiàng)卡效果,很適合大家學(xué)習(xí)和使用,有需要的可以參考借鑒,下面來一起看看吧。2016-09-09IOS實(shí)戰(zhàn)之自定義轉(zhuǎn)場動(dòng)畫詳解
這篇文章主要介紹了IOS實(shí)戰(zhàn)之自定義轉(zhuǎn)場動(dòng)畫,CAAnimation的子類,用于做轉(zhuǎn)場動(dòng)畫,能夠?yàn)閷犹峁┮瞥銎聊缓鸵迫肫聊坏膭?dòng)畫效果,感興趣的小伙伴們可以參考一下2016-02-02IOS開發(fā) 支持https請求以及ssl證書配置詳解
這篇文章主要介紹了IOS開發(fā) 支持https請求以及ssl證書配置詳解的相關(guān)資料,需要的朋友可以參考下2017-02-02React Native搭建iOS開發(fā)環(huán)境
React Native的門檻不管是對于前端開發(fā)者還是移動(dòng)端開發(fā)者來說都是很高的,既要懂原生又要懂js,技術(shù)棧是相當(dāng)長的。但是沒有關(guān)系,下面我們一步步來學(xué)習(xí),慢慢成長吧!2016-09-09