绘制QuartZ 2DiOS动画

Core Graphics框架 : 一个让程序猿成为视觉设计师的

2016-09-08  本文已影响1988人  神经骚栋

Core Graphics简介


说Core Graphics框架之前,我们首先要先说一下,在iOS中绘制图形的方式,其形式主要有三种.

  • 1.创建一个UI视图,并且使用"drawRect:"方法添加到Quartz 2D绘制代码中,也就是今天所要说的Core Graphics框架.
  • 2.使用Core Animation层,并且通过委托方法想Core Animation层提供图形内容.
  • 3.通过 OpenGL ES 渲染图形

其结构图如下所示.从图中我们看到的是是不管使用者还是开发者最容易接触到的先是UIKit框架,然后再就是Core Graphics,Core Animation和OpenGL ES,Core Graphics,Core Animation这两个框架虽然我们没有用过,但是我们最少听过,但是OpenGL ES是什么鬼?OpenGL ES这个有点日后再议,今天主题不是它,而是我们的Core Graphics框架.
</b>
Core Graphics其实是一套基于C语言的API框架,使用了Quartz作为绘图引擎。这也就意味着Core Graphics不是面向对象的。但是这句话对我们并无伤大雅.也就是然并卵~,我们只需要知道如何使用Core Graphics框架就好.下面我就对Core Graphics框架相关知识一一道来.

CGContextRef (图形上下文)


图形上下文,不要被它的名字吓到,其实你就可以理解为是一个画布,我们要绘制任何东西总要有个东西接受我们绘制的东西吧?所以CGContextRef就出现了,对于CGContextRef有几个注意点,iOS是允许新建CGContextRef的,但是我们通常不这么干,因为创建一个新的CGContextRef,但是性能和内存的使用上,效率是非常低的。所以我们会在"drawRect:"方法中直接获取当前View的CGContextRef.代码如下.

    //获取当前View的图形上下文
    CGContextRef contextRef = UIGraphicsGetCurrentContext();

CGPath(路径)


那么画板有了,我们是不是应该可以作图了?是的,但是如何才能确定出一个图形呢?不管是什么图形,首先我们要先确定它的边框,一旦确定了一个边框,我们就可以设置边框的各种绘图属性、边框内部区域的绘图属性、绘制边框还是内部区域等。这时候,CGPath就闪亮登场了.CGPath也就是路径信息,一开始如果没有接触过PS过的童鞋可能不知道路径到底是一个什么,通俗一点讲,不管你画什么图形,直线也好,多边形也罢,你的画笔都要从一个开始位置开始,然后 "持续"移动画笔,画出一个图形来.终止于一个结束点,如果画笔为无色,画笔划过的线可以理解为路径.当然了,现实中哪有无色的画笔?哈哈.好了言归正传.Core Graphics框架其实已给我们分装好了很多的调用函数,方便我们调用,我们就看几个常用调用函数.

新建路径(常用于创建直线,也可以用直接拼接成多边形)

CGMutablePathRef path = CGPathCreateMutable();

新建矩形,rect是矩形的位置和大小信息,transform是仿射变换参数.

CGPathCreateWithRect(CGRect rect,const CGAffineTransform * __nullable transform)

新建圆,rect是圆的位置和大小信息,cornerWidth和cornerHeight是圆的半径,通过设置这两个参数,我们可以生成椭圆,transform是仿射变换参数.

CGPathCreateWithRoundedRect(CGRect rect,CGFloat cornerWidth, CGFloat cornerHeight,const CGAffineTransform * __nullable transform)

把路径添加到图形上下文中,c是图形上下文参数,path是路径参数.这里有一点要注意,如果你创建好路径,并且调用下面添加方法,我们在View页面上是看不到的路径,这是为什么呢?还记得我们前面说用"无色"的画笔做出的图形,可以理解为路径.就是因为没有涂色,所以看不见.

CGContextAddPath(CGContextRef __nullable c,CGPathRef __nullable path)

绘制路径函数,我们把路径的属性设置完成之后,就需要绘制函数,记住添加和绘制是两回事~~~.c代表着图形上下文.mode代表着填充类型.

CGContextDrawPath(CGContextRef __nullable c,  CGPathDrawingMode mode)

释放删除路径函数,有人就会问,我们在ARC开发环境下,会自动释放呀,NONONO~~,因为CGPathCreateMutable方法返回的路径是一个Core Fundation Object.而这并不在ARC的管理范围之内.所以需要使用下面函数手动释放对象. path代表着要删除的路径.

CGPathRelease(CGPathRef __nullable path)

CGAffineTransform(仿射变换)


那么,就算我们已经已经做好了图形了,可能我们需要对图形做平移,缩放,旋转的时候,我们该如何实现?CGAffineTransform是一个仿射变换的结构体,专门用于进行二维平面的几何变换(平移,旋转,缩放).而且比较贴心的是系统(到底是系统还是程序猿?😂)已经帮我们分装了一些函数,我们不需要直接设置CGAffineTransform,直接调用函数即可,下面我们就看一下CGAffineTransform的结构体形式以及一些常用的仿射变换函数.常用函数有一个地方要注意,就是不管是哪一种仿射变换,都有两个函数,一个是在初始的状态上进行变换,一个是在前一个状态上进行变换.

CGAffineTransform的结构体如下所示,如果想要了解CGAffineTransform就要引入齐次函数的概念,这个以后再说.

struct CGAffineTransform {
  CGFloat a, b, c, d;
  CGFloat tx, ty;
};

平移函数,tx和ty代表着偏移量.

 CGAffineTransformMakeTranslation(CGFloat tx,CGFloat ty)

缩放函数,sx和sy代表着x轴和y轴的缩放比例.

CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)

旋转函数,angle代表着旋转的角度.逆时针为正,顺时针为负。

CGAffineTransformMakeRotation(CGFloat angle)

文字的绘制


上面我们看了一下如何创建多边形,那么显示当中不但有图形还会出现文字,文字我们又该如何处理呢?文字的绘制比较简单,我们只要直接使用需要绘制的字符串直接调用drawInRect:drawAtPoint:这两个方法即可.具体内容以后再说,这篇我们只做简单的了解.下面我们就对这两个绘制方法进行简单的介绍.

rect代表着绘制的文字的大小和位置信息.attrs代表着文本属性.

- (void)drawInRect:(CGRect)rect withAttributes:(nullable NSDictionary<NSString *, id> *)attrs 

point代表着绘制的文字的大小信息.attrs代表着文本属性.

- (void)drawAtPoint:(CGPoint)point withAttributes:(nullable NSDictionary<NSString *, id> *)attrs 

重新绘制.


我们知道绘制整体过程是在View的- (void)drawRect:(CGRect)rect;方法中进行.如果我们想要重新绘制图形该如何办呢?我们并不会直接调用drawRect:这个绘制方法,而是调用如下方法.

//重新绘制.
- (void)setNeedsDisplay;

从Core Graphics框架到Hello World


上面喋喋不休的讲了一大堆,可能童鞋早已擦拳磨掌准备大干一场了.接下来我们就开始Hello World吧.

首先,我们在工程中新建一个UIView对象.这里我创建的是SDView,大家可自行设定.

创建好了UIView对象之后,我们就在SDView.h的- (void)drawRect:(CGRect)rect;方法中进行绘制工作,首先我们要先获取View的图形上下文.毕竟自己创建的效率低嘛~

    //获取上下文
    CGContextRef contextRef = UIGraphicsGetCurrentContext();

获取完成之后,Hello World之路就从一条直线开始吧,首先我们先设置创建一个路径.

    //创建路径
    CGMutablePathRef path = CGPathCreateMutable();

创建完路径,我们就设置路径的起始位置和终点位置,并且把它添加上图形上下文上.

    CGPathMoveToPoint(path, nil, 100, 100);
    
    CGPathAddLineToPoint(path, nil, 200, 200);
    
    CGContextAddPath(contextRef, path);

如果现在路径已经做好了,是一条从(100,100)到(200,200)的直线.并且已经添加到图形上下文中了.这时候如果我们直接绘制并不能看到我们的路径这是为什么呢?还是因为"无色"呀,没有设置颜色,我们怎么会看到.所以下面我们就对路径的属性进行相关的设置.

上图中,笔触颜色为红色,其实也就是边框的颜色,因为直线只是一条直线,所以我们设置它的笔触颜色即可.设置代码如下,我们需要输入的是RGB值.

    CGContextSetRGBStrokeColor(contextRef, 1.0, 0, 0, 1);//设置笔触颜色

下面这行代码是设置填充色,意思是填充路径圈起来的内部颜色.直线设置也无伤大雅,因为没有圈起来任何区域,像多边形,就能看出效果来了.

    CGContextSetRGBFillColor(contextRef, 0, 1.0, 0, 1);//设置填充色

设置线条的宽度,如果不进行设置,默认好像设置为1.0

    CGContextSetLineWidth(contextRef, 5.0);//设置线条宽度

对于设置顶点样式和连接点样式,我们需要注意的是它的概念,顶点就是两头的点,即开始节点和终止节点.连接点呢,即为中间转折点,比如创建矩形,就会产生两个连接点.

    CGContextSetLineCap(contextRef, kCGLineCapButt);//设置顶点样式
    CGContextSetLineJoin(contextRef, kCGLineJoinRound);//设置连接点样式

上面呢,我们已经把属性设置完成了,接下来就是绘制了.

绘制路径,因为我们只需要绘制路径部分,所以我们的填充类型选择是kCGPathFillStroke;

    CGContextDrawPath(contextRef, kCGPathFillStroke);//最后一个参数是填充类型
    

最后,我们需要手动释放我们的路径.原因看路径相关部分.

    CGPathRelease(path);

这样整体的Hello world绘制部分就完成了,现在我们需要在ViewController中调用一下,看看我们的绘制成果.代码如下.

#import "ViewController.h"
#import "SDView.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    SDView *view = [[SDView alloc]initWithFrame:self.view.frame];
    view.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:view];

}

@end

最后command +R运行一下,看一下效果图,我们成功的绘制出了我们第一条直线.

Core Graphics优势


说了这么多,现在我们只是知道Core Graphics框架是iOS实现图像的一种方式,但是什么要深入的研究Core Graphics呢?或者说Core Graphics直接绘图有着这样的优势呢?

因为Core Graphics更接近底层,越接近底层就越快速越高效.这自是不用多说,同时呢,使用Core Graphics可以大大减少应用程序的大小,这又是从何说起呢?举个例子来说,如下图所示.我们现在要在屏幕上显示一个文字,通常我们会怎么干呢?我们会先创建一个UILabel对象,然后把文字添加上,我们需要绘制的部分是UILable对象和文字两部分,现在,我们使用Core Graphics可以只绘制文字这一部分,对系统内存的损耗大大减少.再就是仿射变换结构体的存在,我们可以对绘制的部分进行一些平移,缩放,旋转等操作.


</br>


恩恩,Core Graphics框架 ,一个让程序猿成为视觉设计师的框架,hello world之路就到这了.最后附上本文的Demo.后期,我会对Core Graphics框架中所遇到的相关知识进行详细的分析解说,希望大家继续关注骚栋.

--->传送门💾

上一篇下一篇

猜你喜欢

热点阅读