文章目录
简介
核心绘图:
当一个视图View显示在屏幕上时,其展现给用户的样式都是经过系统绘制后显示在屏幕上的。例如,UILabel控件能够在控件所在矩形区域的中间/左侧/右侧显示文字,UIImageView可以显示一张图片,UIButton可以显示图片加文字,这些控件最终展示的效果其实都是经过绘图后才显示出来的,而绘图的过程可以由程序员自行控制。也正因为如此,我们可以去控制绘图的过程,从而可以使视图展示不同的效果。这就需要我们去学习CoreGraphics框架。
Quartz2D
CoreGraphics中最关键的部分是一个名为Quartz 2D的API集合,它包含了各种函数、数据类型以及对象,能够让大家在内存中直接绘制视图和图像。在使用CoreGraphics框架时,需要提前了解在绘图过程中涉及的几个重要概念。
画布(page)
Quartz 2D将正在进行绘制的视图视作一个虚拟的画布,在画布上用画笔画画,就必须遵循一定的规则,例如,绘制的内容是有先后顺序的,如下图所示。后画的会把之前画的覆盖掉。
路径(Path)
当你拿到一只画笔时,那么就能够随心所欲的在画纸上绘画,画笔移动的轨迹就是路径。如下图所示,一只粉红色的画笔绘制了两个图形,中间使用蓝色进行了填充。在UIKit框架中,有一个称为贝塞尔路径(UIBezierPath)的类专门用来设置各种样式的路径对象。
绘图上下文(Graphics Context)
绘图上下文,有时也称为绘图环境,绘图上下文中会保存绘图的信息和状态,并负责将图形绘制在视图上,即绘图的输出终端(Drawing Destination),Quartz提供了5种绘图的输出目标,如下图所示。例如,可以输出到窗口屏幕window,也可以输出到打印机printer,也可以保存为文件PDF等。但我们在开发中最经常使用的是layer,后续会详细介绍。
绘图原理
有关Quartz2D的绘图原理和我们日常生活中绘图是一致的。例如,我们拿出一张纸,以及一支笔,那么就可以使用这支笔在纸上画出各种线条(路径),线条的样式,例如:线条的粗细以及颜色,则取决于画笔;而路径则取决于绘画者的意愿。当线条或者图画之间有交叉或者覆盖时,最新绘画的内容会覆盖之前的内容。
使用Quartz2D进行图形绘制时,通常需要向图形所在的视图中添加绘图代码。比如创建一个UIView的子类,并向该类的drawRect:方法中添加Quartz函数的调用,重写系统默认的drawRect方法,相当于在系统原来的绘图基础上,再进行新的绘图。
注意一个特殊情况:UIImageView的子类不会调用drawRect方法
绘图中经常使用的方法
在使用CoreGraphics框架对视图进行绘图操作时,UIView中如下的几个方法需要程序员重点关注,这涉及到绘图的时机。
drawRect:方法:当视图每次需要进行自身重新绘制时都会调用该方法,所以如果在drawRect:方法中插入视图绘制的代码,那么这段绘图代码就会起效,从而对视图进行重绘。该方法会被系统自动调用。
- (void)drawRect:(CGRect)rect;
setNeedDisplay方法:当手工调用这个方法时,会调用drawRect:方法进行绘图,但该方法是异步执行的,即不是立即调用drawRect:方法进行绘图,而是在屏幕每次刷新后才会调用。 - (void)setNeedsDisplay;
贝塞尔路径(UIBezierPath)
UIBezierPath类属于UIKit框架,主要用于绘图中设置绘图的路径。在Core Graphics中,也提供了CGPath类以及CGContext类可以用于绘图。在实际开发中,UIBezierPath类的使用比CGPath类和CGContext类要简单,所以我们重点来学习UIBezierPath类在绘图中的应用。
常用方法
UIBezierPath类主要用于创建一个路径对象,因此在UIBezierPath类中提供了创建各种类型路径的方法,例如直线路径、曲线路径等等。
实例化一个贝塞尔路径对象
+(UIBezierPath *)bezierPath;
初始化矩形路径
+(UIBezierPath *)bezierPathWithRect:(CGRect)rect;
初始化一个圆角的矩形路径
+(instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;
初始化椭圆形路径
+(UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect;
初始化弧形路径。需要传入四个参数,包括弧形的圆心点、弧度以及起始、终止角度
+(UIBezierPath *)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
设置当前绘图所在的点位,即设置画笔当前的位置
-(void)moveToPoint:(CGPoint)point;
从绘图当前点位绘制一条直线到目标点位
-(void)addLineToPoint:(CGPoint)point;
从绘图当前点位绘制一条圆弧到目标点位,弧度需要由控制点controlPoint决定
-(void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
封闭当前的路径
-(void)closePath;
填充路径所封闭的区域
-(void)fill;
画线
-(void)stroke;
绘图方法和步骤
drawRect:方法的调用时机
drawRect:方法是UIView类定义中的方法,视图每次需要进行自身重新绘制时都会调用该方法,所以如果在drawRect:方法中插入绘图代码,那么这段绘图代码就会起效,从而对视图进行重绘。
drawRect:方法主要在如下几个情形下会被调用。
当视图第一次显示到屏幕之前;
当调用视图的setNeedDisplay或者setNeedDisplayInRect:方法,通知重新绘图时。
调用sizeToFit方法后,会调用drawRect:方法。
常见的绘图步骤
当在drawRect:方法中进行绘图时,一般会遵循如下的步骤进行:
获得图形上下文(取得画布),需要调用UIGraphicsGetCurrentContext()函数;
设置路径(绘图),通常使用贝塞尔路径;
设置绘图上下文的属性与状态,如是否填充,填充颜色等等;
添加路径到上下文,调用CGContextAddPath()函数;
渲染上下文,调用CGContextStrokePath()函数,输出绘图图像到屏幕。
绘图示例
绘制一个封闭的三角形,该三角形边线的颜色是红色,线宽是3px,填充颜色是黄色。
-(void)myDrawTriangle{
//1.获取绘图上下文
CGContextRef context=UIGraphicsGetCurrentContext();
//2.设置绘图路径
//2.1创建路径
UIBezierPath *path=[UIBezierPath bezierPath];
//2.2创建起始点
CGPoint startPoint=CGPointMake(5, 5);
CGPoint secondPoint=CGPointMake(50, 95);
CGPoint thirdPoint=CGPointMake(95, 5);
//2.3画线
[path moveToPoint:startPoint];
[path addLineToPoint:secondPoint];
[path addLineToPoint:thirdPoint];
//2.4设置线的属性
[[UIColor redColor] setStroke];
[[UIColor yellowColor]setFill];
[path fill];
//3.设置边线的宽度
CGContextSetLineWidth(context, 3.0);
//4.添加路径到上下文
CGContextAddPath(context, path.CGPath);
//5.渲染上下文
CGContextStrokePath(context);
}
常见图像的绘制方法
绘图的样式都是由路径来决定的。
创建继承自UIView的自定义类,在类中重写drawRect:方法,在该方法中添加绘图相关的代码。
绘制直线
假如需要绘制直线,需要绘制一个贝塞尔路径对象,并指定路径的开始点和结束点,然后在开始点和结束点之间加一条线。
使用自定义的UI类,要设置背景和尺寸,不然看不出来。
-(void)myDrawLine{
//重写绘图方法
//获取上下文/环境
CGContextRef context=UIGraphicsGetCurrentContext();
//设置画笔宽度
CGContextSetLineWidth(context, 3.0);
//设置画笔颜色
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
//绘图
//创建绘图路径
UIBezierPath *path=[UIBezierPath bezierPath];
//设置起始点
[path moveToPoint:CGPointZero];
[path addLineToPoint:CGPointMake(200, 200)];
//添加到上下文
CGContextAddPath(context, path.CGPath);
//渲染上下文
CGContextStrokePath(context);
}
绘制矩形
-(void)myDrawRect{
//获取上下文/环境
CGContextRef context=UIGraphicsGetCurrentContext();
//设置画笔宽度
CGContextSetLineWidth(context, 3.0);
//设置画笔颜色
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
//绘图
//设置矩形的位置和大小
CGRect myrect=CGRectMake(50, 50, 100, 100);
//创建绘图路径
UIBezierPath *path=[UIBezierPath bezierPathWithRect:myrect];
[[UIColor yellowColor]setFill];
[path fill];
//添加到上下文
CGContextAddPath(context, path.CGPath);
//渲染上下文
CGContextStrokePath(context);
}
绘制圆形/椭圆形
通过矩形的框,创建一个椭圆形。当矩形是正方形,得到的就是圆形。
-(void)myDrawEllipse{
//获取上下文/环境
CGContextRef context=UIGraphicsGetCurrentContext();
//设置画笔宽度
CGContextSetLineWidth(context, 3.0);
//设置画笔颜色
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
//绘图
//设置矩形的位置和大小
CGRect myrect=CGRectMake(50, 50, 200, 100);
//创建绘图路径
UIBezierPath *path=[UIBezierPath bezierPathWithOvalInRect:myrect];
[[UIColor yellowColor]setFill];
[path fill];
//添加到上下文
CGContextAddPath(context, path.CGPath);
//渲染上下文
CGContextStrokePath(context);
}