iOS的繪圖框架有很多種,我們平常最常用的就是UIKit,其底層是依賴CoreGraphics實現(xiàn)的昨悼,而且絕大多數(shù)的圖形界面也都是由UIKit完成的蝗锥,并且UIImage、NSString率触、UIBezierPath玛追、UIColor等都知道如何繪制自己,也提供了一些方法來滿足我們常用的繪圖需求。除了UIKit痊剖,還有CoreGraphics韩玩、Core Animation,Core Image陆馁,OpenGL ES等多種框架找颓,來滿足不同的繪圖要求。各個框架的大概介紹如:
1.->UIKit:最常用的視圖框架叮贩,封裝度高击狮,都是OC對象
2.->CoreGraphics:主要繪制系統(tǒng),常用于繪制自定義視圖益老,純C的API彪蓬,使用Quartz2D做引擎
3.->Core Animation:提供強大的2D和3D動畫效果
4.->Core Image:給圖片提供各種濾鏡處理,比如高斯模糊捺萌、銳化等
5.->OpenGL ES:主要用于游戲繪制档冬,但他是一套編程規(guī)范,具體由設(shè)備制造商實現(xiàn)
繪圖方式:
繪圖包括兩部分:視圖繪制和視圖布局桃纯。他們實現(xiàn)的功能是不同的酷誓,因為都是在繪制周期中進行繪制的
繪圖周期:1.iOS在運行循環(huán)中會整合所有的繪圖請求,并一次將它們繪制出來? 2.不能在子線程中繪制态坦,也不能進行復(fù)雜的操作盐数,否則會造成主線程卡頓
視圖繪制:調(diào)用UIView的- (void)drawRect:(CGRect)rect方法進行繪制。如果調(diào)用一個視圖的-(void)setNeedsDisplay方法伞梯,那么該視圖就被標記為重新繪制玫氢,并且會在下一次繪制周期中重新繪制,自動調(diào)用- (void)drawRect:(CGRect)rect方法
視圖布局:調(diào)用UIView的-(void)layoutSubviews方法谜诫。如果調(diào)用一個視圖的-(void)setNeedsLayout方法漾峡,那么該視圖就被標記為需要重新布局,UIKit會自動調(diào)用-(void)layoutSubviews方法及其子視圖的-(void)layoutSubviews
重點:在繪圖時猜绣,我們應(yīng)該盡量多的使用布局,少使用繪制敬特,是因為布局使用的是GPU掰邢,而繪制使用的是CPU。GPU對于圖形處理有優(yōu)勢伟阔,而CPU要處理的事情較多辣之,且不擅長處理圖形,所以盡量使用GPU來處理圖形皱炉。
繪圖狀態(tài)切換
iOS的繪圖有多種對應(yīng)的狀態(tài)切換怀估,比如:比如:pop/push、save/restore、context/imageContext和CGPathRef/UIBezierPath等多搀,下面分別進行介紹:
1.->pop/push
設(shè)置繪圖的上下文環(huán)境(context)
push:UIGraphicsPushContext(context)把context壓入棧中歧蕉,并把context設(shè)置為當前繪圖上下文
pop:UIGraphicsPopContext將棧頂?shù)纳舷挛膹棾觯謴?fù)先前的上下文康铭,但是繪圖狀態(tài)不變
下面的繪圖是黑色
- (void)drawRect:(CGRect)rect{
[[UIColor redColor]setFill];
UIGraphicsPushContext(UIGraphicsGetCurrentContext());
[[UIColor blackColor]setFill];
UIGraphicsPopContext();
UIRectFill(CGRectMake(90, 340, 100, 100));
}
2.save / restore
設(shè)置繪圖的狀態(tài)(state)
save:CGContextSaveGState 壓棧當前的繪圖狀態(tài)惯退,僅僅是繪圖狀態(tài),不是繪圖上下文
restore:恢復(fù)剛才保存的繪圖狀態(tài)
下面繪制的視圖是紅色
- (void)drawRect:(CGRect)rect{
[[UIColor redColor]setFill];
CGContextSaveGState(UIGraphicsGetCurrentContext());
[[UIColor blackColor]setFill];
CGContextRestoreGState(UIGraphicsGetCurrentContext());
UIRectFill(CGRectMake(90, 200, 100, 100));
}
3.context/imageContext
iOS的繪圖必須在一個上下文中繪制从藤,所以在繪制之前要獲取一個上下文催跪。如果是繪制圖片,就需要獲取一個圖片的上下文夷野;如果是繪制其他視圖懊蒸,就需要一個非圖片上下文。對于上下文的理解悯搔,可以認為就是一張畫布骑丸,然后在上面進行繪圖操作。
context:圖形上下文鳖孤,可以通過UIGraphicsGetCurrentContext()獲取當前視圖的上下文
imageContext:圖片上下文者娱,可以通過UIGraphicsBeginImageContextWithOptions:獲取一個圖片上下文,然后繪制完成后苏揣,調(diào)用UIGraphicsGetImageFromCurrentImageContext獲取繪制的圖片黄鳍,最后要記得關(guān)閉圖片上下文UIGraphicsEndImageContext。
4.CGPathRef / UIBezierPath
圖形的繪制需要繪制一個路徑平匈,然后再把路徑渲染出來框沟,而CGPathRef就是CoreGraphics框架中的路徑繪制類,UIBezierPath是封裝CGPathRef的面向OC的類增炭,使用更加方便忍燥,但是一些高級特性還是不及CGPathRef。
具體繪圖方法
由于iOS常用的繪圖框架有UIKit和CoreGraphics兩個隙姿,所以繪圖的方法也有多種梅垄。
1.圖片類型的上下文:是不需要在- (void)drawRect:(CGRect)rect方法中進行,在一個普通的OC方法中就可以繪制
2.使用UIKit實現(xiàn)
//獲取圖片上下文
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, 0);
//繪圖
UIBezierPath* bezierPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 100)];
[[UIColor blueColor]setFill];
[bezierPath fill];
//從圖片上下文中獲取繪制的圖片
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
//關(guān)閉圖片上下文
UIGraphicsEndImageContext();
UIImageView* imageView = [[UIImageView alloc]init];
imageView.frame = CGRectMake(0, 64, image.size.width, image.size.height);
[self.view addSubview:imageView];
imageView.image = image;
使用CoreGraphics實現(xiàn):
//獲取圖片上下文
UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, 0);
//繪圖
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(contextRef, CGRectMake(0, 0, 100, 100));
CGContextSetFillColorWithColor(contextRef, [UIColor blueColor].CGColor);
CGContextFillPath(contextRef);
//從圖片上下文中獲取繪制的圖片
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
//關(guān)閉圖片上下文
UIGraphicsEndImageContext();
UIImageView* imageView = [[UIImageView alloc]init];
[self.view addSubview:imageView];
imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height);
imageView.image = image;
2.- (void)drawRect:(CGRect)rect
在UIView子類的- (void)drawRect:(CGRect)rect方法中實現(xiàn)圖形重新繪制输玷,具體繪制步驟如下:
2.1->獲取上下文
2.2->繪制圖形
2.3->渲染圖形
UIKit方法
- (void)drawRect:(CGRect)rect{
UIBezierPath* bezierPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 100)];
[[UIColor blueColor]setFill];
[bezierPath fill];
}
CoreGraphics
- (void)drawRect:(CGRect)rect{
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(contextRef, CGRectMake(0, 0, 100, 100));
CGContextSetFillColorWithColor(contextRef, [UIColor blueColor].CGColor);
CGContextFillPath(contextRef);
}
3.drawLayer:inContext:
在UIView子類的-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx方法中也可以實現(xiàn)繪圖任務(wù)队丝,他是一個圖層的代理方法,當調(diào)用該方法的時候欲鹏,需要給圖層的delegate設(shè)置代理對象机久。但是代理對象不能是UIView對象,因為UIView對象內(nèi)部已經(jīng)是它內(nèi)部根層的代理對象赔嚎,如果再將他設(shè)置為另一個層的代理對象就會出問題了
一個view被添加到其它view上時膘盖,圖層的變化如下:
3.1->先隱式地把此view的layer的CALayerDelegate設(shè)置成此view
3.2->調(diào)用此view的self.layer的drawInContext方法
3.3->由于drawLayer方法的注釋:If defined, called by the default implementation of -drawInContext:說明了drawInContext里if([self.delegate responseToSelector:@selector(drawLayer:inContext:)])就執(zhí)行drawLayer:inContext:方法胧弛,這里我們因為實現(xiàn)了drawLayer:inContext:所以會執(zhí)行
3.4->[super drawLayer:layer inContext:ctx]會讓系統(tǒng)自動調(diào)用此view的drawRect:方法,至此self.layer畫出來了
3.5->在self.layer上再加一個子layer侠畔,當調(diào)用[layer setNeedsDisplay];時會自動調(diào)用此layer的drawInContext方法
3.6->如果drawRect不重寫结缚,就不會調(diào)用其layer的drawInContext方法,也就不會調(diào)用drawLayer:inContext方法