Core Graphics框架 : 一个让程序猿成为视觉设计师的
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框架中所遇到的相关知识进行详细的分析解说,希望大家继续关注骚栋.