引言
在iOS動畫篇:核心動畫中講到如何給一個視圖添加動畫效果,但是其僅局限在系統控件的具有動畫效果的屬性。假設現在我們要做一個空心圓形的進度條,隨著進度的變化具有對應的動畫效果,這時候就需要去自定義一個圓形的View,並實現其形狀隨進度屬性的變化而變化,使用Quartz2D就可以輕松滿足此需求。
什麼是Quartz2D
Quartz2D是iOS和OSX中的一個二維繪圖引擎,這組API具有許多強大的功能,如:圖形的繪制、透明層、陰影、顏色管理、反鋸齒、PDF文檔的管理等等。
本文主要介紹了Quartz2D主要相關概念,描述其中的圖形繪制部分(通過路徑繪制圖形),以實現自定義View。本文不對Quartz2D的基礎過多提及,如果讀者需要更深入了解Quartz2D,可以Google"Quartz2D編程指南"研讀Quartz2D系列譯文。
相關概念
使用Quartz2D來繪制圖形,需要知道的相關概念:
1、Core Graphics
Core Graphic是一套基於C的框架,用於一切繪圖操作,UIKit就是基於Core Graphic實現的,因此它可以實現比UIKit更底層的功能。
Core Graphic
Core Graphic使用Quartz2D作為繪圖引擎,因此Quartz2D其實是Core Graphic的一部分,這兩個名詞密不可分。
2、圖形上下文
畫畫需要畫布,Core Graphics工作是的“畫布”就是圖形上下文,它決定圖形繪制成什麼樣子,並繪制到哪裡去。在“畫布”中,每個連續的繪制操作都可以看成添加一個“圖層”到畫布上,在運行中我們可以通過額外的繪制操作來疊加更多“圖層”來形成復雜的圖形。
推薦使用UIView自動為我們准備好的圖形上下文,因為自定義上下文會降低內存的使用效率,導致性能下降。當需要我們調用UIGraphicsGetCurrentContext()來獲取圖形上下文。
3、路徑
相信很多人都臨摹過書法,在一張薄薄的紙上照著書法家的筆跡來書寫,這個“筆跡”就可以看成路徑,通過確定的路徑,可以確定繪圖的形狀、渲染的區域等等。
通過創建路徑並加入到上下文中渲染就能繪制出想要的圖形。
創建路徑有以下三種方式:
1.使用CGContextRef創建,如CGContextAddArc
這種方式是直接對圖形上下文進行操作,常用的方法有:
CGContextBeginPath //開始畫路徑 CGContextMoveToPoint //移動到某一點 CGContexAddLineToPoint //畫直線 CGContexAddCurveToPoint //畫餅圖 CGContexAddEllipseInRect //畫橢圓 CGContexAddArc //畫圓 CGContexAddRect //畫方框 CGContexClosePath //封閉當前路徑
2.使用CGPathRef創建,如CGPathAddArc
使用方法一繪制路徑後將清空圖形上下文,如果我們想保存路徑來復用,可以使用Quartz提供的CGPath函數集合來創建可復用的路徑對象。
常用的函數如下:
CGPathCreateMutable CGPathMoveToPoint CGPathAddLineToPoint CGPathAddCurveToPoint CGPathAddEllipseInRect CGPathAddArc CGPathAddRect CGPathCloseSubpath
這些函數和上面方法一的一一對應,可代替之使用。
CGContextAddPath:添加一個新的路徑
3.使用UIBezierPath創建,如bezierPathWithOvalInRect
UIBezierPath存在於UIKit中,是對路徑繪制的封裝,和CGContextRef類似,優點是更面向對象,我們可以像操作普通對象一樣對其進行操作。
在自定義View的時候,一般使用UIBezierPath來創建路徑就能基本滿足我們的需求,推薦使用。
UIBezierPath的常用方法如下:
@property(nonatomic) CGFloat lineWidth; //線的寬度 @property(nonatomic) CGLineCap lineCapStyle; //起點和終點樣式 @property(nonatomic) CGLineJoin lineJoinStyle; //轉角樣式 //創建path + (instancetype)bezierPath; //矩形 + (instancetype)bezierPathWithRect:(CGRect)rect; //以矩形框為切線畫圓 + (instancetype)bezierPathWithOvalInRect:(CGRect)rect; //帶圓角的矩形框 + (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius; // rounds all corners with the same horizontal and vertical radius //畫圓弧 + (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise; //移動到某一點 - (void)moveToPoint:(CGPoint)point; //添加直線 - (void)addLineToPoint:(CGPoint)point; //帶一個基准點的曲線 - (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint; //帶兩個基准點的曲線 - (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2; //封閉路徑 - (void)closePath; //添加新的路徑 - (void)appendPath:(UIBezierPath *)bezierPath; //渲染 - (void)fill; - (void)stroke;
4、渲染
繪畫的最後一步,它之於繪圖的意義如畫畫的最後上顏料一樣。
渲染分為兩種方式:
1)填充Fill:將路徑內部填充渲染
2)描邊Stroke:不填充,只對路徑進行渲染
5、繪圖狀態棧
圖形上下文包含一個繪圖狀態棧,默認為空。
1)保存圖形狀態時,將創建當前圖形狀態的一個副本並入棧。
2)還原圖形狀態時,將棧頂的圖形狀態推出棧並替換當前圖形狀態。
使用:調用CGContextSaveGState來保存,CGContextRestoreGState來還原。
繪圖的核心步驟
在view上繪制一個圖形的方式有很多種,表現形式可能不一樣,但其實質步驟都是一樣的:
1)獲取上下文
2)繪制路徑
3)添加路徑到上下文
4)修改圖形狀態參數
5)渲染上下文
下面我們以畫一個圓形來演示其實現步驟:
1)使用CGContextRef創建路徑
//獲取上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); //繪制路徑: 圓形(中心坐標200、200、半徑100、起點弧度0、終點弧度2PI、畫的方向0逆1正) CGContextAddArc(ctx, 200, 200, 100, 0, M_PI * 2, 0); //修改圖形狀態參數 CGContextSetRGBStrokeColor(ctx, 0.5, 0.5, 0.9, 1.0);//筆顏色 CGContextSetLineWidth(ctx, 10);//線條寬度 //渲染上下文 CGContextStrokePath(ctx);
2)使用CGPathRef創建路徑
//獲取上下文 CGContextRef ctx = UIGraphicsGetCurrentContext(); //創建可變路徑 CGMutablePathRef path = CGPathCreateMutable(); //添加圓形到路徑中(所在路徑、不進行變換操作、中心坐標200、200、起點弧度0、終點弧度2PI、畫的方向0逆1正) CGPathAddArc(path, NULL, 200, 200, 100, 0, M_PI * 2, 1); //將路徑添加到上下文 CGContextAddPath(ctx, path); //修改圖形狀態參數 CGContextSetRGBStrokeColor(ctx, 0.5, 0.5, 0.9, 1.0);//筆顏色 CGContextSetLineWidth(ctx, 10);//線條寬度 //渲染上下文 CGContextStrokePath(ctx);
3)使用UIBezierPath創建路徑
//創建路徑 UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 200, 200)]; //修改圖形狀態參數 [[UIColor colorWithRed:0.5 green:0.5 blue:0.9 alpha:1.0] setStroke];//筆顏色 [path setLineWidth:10];//線條寬度 //渲染 [path stroke];
以上三種方式都可以實現繪制,通過比較我們可以發現使用UIBezierPath創建路徑的形式是最簡潔且最直觀的,推薦使用UIBezierPath,在以後的動畫中我們也將更多地應用UIBezierPath到動畫的實現中。
自定義view的步驟
只需簡單兩步即可:
步驟一:新建一個類,繼承UIView類。
步驟二:重載drawRect方法,在這個方法中進行繪圖。
以自定義一個圓形View為例:
1)新建CircleView類,繼承UIView類
CircleView.png
2)在CircleView.m中重載drawRect方法
- (void)drawRect:(CGRect)rect { }
3)畫一個圓
- (void)drawRect:(CGRect)rect { UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 200, 200)]; [[UIColor colorWithRed:0.5 green:0.5 blue:0.9 alpha:1.0] setStroke]; [path setLineWidth:10]; [path stroke]; }
4)創建CircleView的實例添加到視圖中
- (void)viewDidLoad { [super viewDidLoad]; CircleView * cricleView = [[CircleView alloc]initWithFrame:self.view.bounds]; [self.view addSubview:cricleView]; }
5)效果圖
效果圖.png
成功畫了一個圓形,現在只差怎樣讓它“動起來”了!
思考
1、Quartz2D的坐標系和UIView的坐標系有什麼不同?
2、繪制圖形時不同路徑使用不同的狀態參數渲染需要怎樣操作?
3、怎樣使用CALayer來自定義View?
文/明仔Su(簡書作者)
原文鏈接:http://www.jianshu.com/p/9ac974756f77