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:添加一個新的路徑
2.使用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?

next:

如何在自定義View上實現動畫效果

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Core Graphics Framework是一套基于C的API框架,使用了Quartz作為繪圖引擎。它提供了低...
    ShanJiJi閱讀 1,601評論 0 20
  • --繪圖與濾鏡全面解析 概述 在iOS中可以很容易的開發出絢麗的界面效果,一方面得益于成功系統的設計,另一方面得益...
    韓七夏閱讀 2,796評論 2 10
  • Quartz2D以及drawRect的重繪機制字數1487 閱讀21 評論1 喜歡1一、什么是Quartz2D Q...
    PurpleWind閱讀 810評論 0 3
  • 無論是長篇小說還是短篇小說,標題是吸引我讀它的首要因素,而每次讀完我也會想想這篇文章到底什么地方扣題?對于這本《無...
    露琪亞閱讀 466評論 2 5