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上实现动画效果

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Core Graphics Framework是一套基于C的API框架,使用了Quartz作为绘图引擎。它提供了低...
    ShanJiJi阅读 1,513评论 0 20
  • --绘图与滤镜全面解析 概述 在iOS中可以很容易的开发出绚丽的界面效果,一方面得益于成功系统的设计,另一方面得益...
    韩七夏阅读 2,708评论 2 10
  • Quartz2D以及drawRect的重绘机制字数1487 阅读21 评论1 喜欢1一、什么是Quartz2D Q...
    PurpleWind阅读 760评论 0 3
  • 无论是长篇小说还是短篇小说,标题是吸引我读它的首要因素,而每次读完我也会想想这篇文章到底什么地方扣题?对于这本《无...
    露琪亚阅读 433评论 2 5