iOS動畫篇:自定義View
引言
在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;</code></pre>
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類

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)效果圖

成功畫了一個圓形,現在只差怎樣讓它“動起來”了!
思考
1、Quartz2D的坐標系和UIView的坐標系有什么不同?
2、繪制圖形時不同路徑使用不同的狀態參數渲染需要怎樣操作?
3、怎樣使用CALayer來自定義View?
來自: http://www.cocoachina.com/ios/20160518/16293.html