iOS动画篇:自定义View

bsgh5998 9年前
   <h2>引言</h2>    <p>在iOS动画篇:核心动画中讲到如何给一个视图添加动画效果,但是其仅局限在系统控件的具有动画效果的属性。假设现在我们要做一个空心圆形的进度条,随着进度的变化具有对应的动画效果,这时候就需要去自定义一个圆形的View,并实现其形状随进度属性的变化而变化,使用Quartz2D就可以轻松满足此需求。</p>    <h2>什么是Quartz2D</h2>    <p>Quartz2D是iOS和OSX中的一个二维绘图引擎,这组API具有许多强大的功能,如:图形的绘制、透明层、阴影、颜色管理、反锯齿、PDF文档的管理等等。</p>    <p>本文主要介绍了Quartz2D主要相关概念,描述其中的图形绘制部分(通过路径绘制图形),以实现自定义View。本文不对Quartz2D的基础过多提及,如果读者需要更深入了解Quartz2D,可以Google"Quartz2D编程指南"研读Quartz2D系列译文。</p>    <h2>相关概念</h2>    <p>使用Quartz2D来绘制图形,需要知道的相关概念:</p>    <p>1、Core Graphics</p>    <p>Core Graphic是一套基于C的框架,用于一切绘图操作,UIKit就是基于Core Graphic实现的,因此它可以实现比UIKit更底层的功能。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b593918ccd20434a2e4ec67fd14c1f54.png"></p>    <p style="text-align:center">Core Graphic</p>    <p>Core Graphic使用Quartz2D作为绘图引擎,因此Quartz2D其实是Core Graphic的一部分,这两个名词密不可分。</p>    <p>2、图形上下文</p>    <p>画画需要画布,Core Graphics工作是的“画布”就是图形上下文,它决定图形绘制成什么样子,并绘制到哪里去。在“画布”中,每个连续的绘制操作都可以看成添加一个“图层”到画布上,在运行中我们可以通过额外的绘制操作来叠加更多“图层”来形成复杂的图形。</p>    <p>推荐使用UIView自动为我们准备好的图形上下文,因为自定义上下文会降低内存的使用效率,导致性能下降。当需要我们调用UIGraphicsGetCurrentContext()来获取图形上下文。</p>    <p>3、路径</p>    <p>相信很多人都临摹过书法,在一张薄薄的纸上照着书法家的笔迹来书写,这个“笔迹”就可以看成路径,通过确定的路径,可以确定绘图的形状、渲染的区域等等。</p>    <p>通过创建路径并加入到上下文中渲染就能绘制出想要的图形。</p>    <h2>创建路径有以下三种方式:</h2>    <p>1.使用CGContextRef创建,如CGContextAddArc</p>    <p>这种方式是直接对图形上下文进行操作,常用的方法有:</p>    <pre>  <code class="language-objectivec">CGContextBeginPath //开始画路径     CGContextMoveToPoint //移动到某一点     CGContexAddLineToPoint //画直线     CGContexAddCurveToPoint //画饼图     CGContexAddEllipseInRect //画椭圆     CGContexAddArc //画圆     CGContexAddRect //画方框     CGContexClosePath //封闭当前路径</code></pre>    <p>2.使用CGPathRef创建,如CGPathAddArc</p>    <p>使用方法一绘制路径后将清空图形上下文,如果我们想保存路径来复用,可以使用Quartz提供的CGPath函数集合来创建可复用的路径对象。</p>    <p>常用的函数如下:</p>    <pre>  <code class="language-objectivec">CGPathCreateMutable     CGPathMoveToPoint     CGPathAddLineToPoint     CGPathAddCurveToPoint     CGPathAddEllipseInRect     CGPathAddArc     CGPathAddRect     CGPathCloseSubpath</code></pre>    <p>这些函数和上面方法一的一一对应,可代替之使用。</p>    <pre>  <code class="language-objectivec">CGContextAddPath:添加一个新的路径</code></pre>    <p>3.使用UIBezierPath创建,如bezierPathWithOvalInRect</p>    <p>UIBezierPath存在于UIKit中,是对路径绘制的封装,和CGContextRef类似,优点是更面向对象,我们可以像操作普通对象一样对其进行操作。</p>    <p>在自定义View的时候,一般使用UIBezierPath来创建路径就能基本满足我们的需求,推荐使用。</p>    <p>UIBezierPath的常用方法如下:</p>    <pre>  <code class="language-objectivec">@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>    <p>4、渲染</p>    <p>绘画的最后一步,它之于绘图的意义如画画的最后上颜料一样。</p>    <p>渲染分为两种方式:</p>    <p>1)填充Fill:将路径内部填充渲染</p>    <p>2)描边Stroke:不填充,只对路径进行渲染</p>    <p>5、绘图状态栈</p>    <p>图形上下文包含一个绘图状态栈,默认为空。</p>    <p>1)保存图形状态时,将创建当前图形状态的一个副本并入栈。</p>    <p>2)还原图形状态时,将栈顶的图形状态推出栈并替换当前图形状态。</p>    <p>使用:调用CGContextSaveGState来保存,CGContextRestoreGState来还原。</p>    <h2>绘图的核心步骤</h2>    <p>在view上绘制一个图形的方式有很多种,表现形式可能不一样,但其实质步骤都是一样的:</p>    <p>1)获取上下文</p>    <p>2)绘制路径</p>    <p>3)添加路径到上下文</p>    <p>4)修改图形状态参数</p>    <p>5)渲染上下文</p>    <p>下面我们以画一个圆形来演示其实现步骤:</p>    <p>1)使用CGContextRef创建路径</p>    <pre>  <code class="language-objectivec"> //获取上下文   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);</code></pre>    <p>2)使用CGPathRef创建路径</p>    <pre>  <code class="language-objectivec"> //获取上下文   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);</code></pre>    <p>3)使用UIBezierPath创建路径</p>    <pre>  <code class="language-objectivec"> //创建路径   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];</code></pre>    <p>以上三种方式都可以实现绘制,通过比较我们可以发现使用UIBezierPath创建路径的形式是最简洁且最直观的,推荐使用UIBezierPath,在以后的动画中我们也将更多地应用UIBezierPath到动画的实现中。</p>    <p>自定义view的步骤</p>    <p>只需简单两步即可:</p>    <p>步骤一:新建一个类,继承UIView类。</p>    <p>步骤二:重载drawRect方法,在这个方法中进行绘图。</p>    <p>以自定义一个圆形View为例:</p>    <p>1)新建CircleView类,继承UIView类</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/669c3734de18942d150035fcfc9f228d.jpg"></p>    <p> </p>    <p>2)在CircleView.m中重载drawRect方法</p>    <pre>  <code class="language-objectivec">  - (void)drawRect:(CGRect)rect {    }</code></pre>    <p>3)画一个圆</p>    <pre>  <code class="language-objectivec">  - (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];    }</code></pre>    <p>4)创建CircleView的实例添加到视图中</p>    <pre>  <code class="language-objectivec">- (void)viewDidLoad {        [super viewDidLoad];        CircleView * cricleView = [[CircleView alloc]initWithFrame:self.view.bounds];        [self.view addSubview:cricleView];    }</code></pre>    <p>5)效果图</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/4264a0c1f761f372b6e0c02338e7df6f.jpg"></p>    <p> </p>    <p>成功画了一个圆形,现在只差怎样让它“动起来”了!</p>    <h2>思考</h2>    <p>1、Quartz2D的坐标系和UIView的坐标系有什么不同?</p>    <p>2、绘制图形时不同路径使用不同的状态参数渲染需要怎样操作?</p>    <p>3、怎样使用CALayer来自定义View?</p>    <p> </p>    <p>来自: <a href="/misc/goto?guid=4959673303773319870" rel="nofollow">http://www.cocoachina.com/ios/20160518/16293.html</a></p>    <p> </p>