iOS DevQuartZ 2D

iOS开发-Quartz 2D基础篇

2016-10-17  本文已影响1436人  Eiwodetianna

Quartz 2D是一个二维图形绘制引擎,支持iOS环境和Mac OS X环境。我们可以使用Quartz 2D API来实现许多功能,如基本路径的绘制、透明度、描影、绘制阴影、透明层、颜色管理、反锯齿、PDF文档生成和PDF元数据访问。

绘制原理

Quartz 2D在图像绘制中使用了绘画者模型。在绘画者模型中,每个连续的绘制操作都是将一个绘制层放置于一个画布(canvas),我们可以理解为图像的绘制即为在画布上绘画,而绘画操作的顺序同时影响着我们绘制的结果,如图:

图形上下文(Graphics Context)

一个Graphics Context表示一个绘制目标。它包含完成绘制任务所需的一些绘制参数和设备相关信息。Graphics Context定义了基本的绘制属性,如颜色、裁减区域、线条宽度和字体信息、混合模式等。

Quartz 2D 坐标系

Quartz 2D中默认的坐标系统为:左下角为坐标系统原点(0,0),屏幕水平方向为x轴,沿着x轴从左到右为正方向;屏幕垂直方向为y轴,沿着y轴从下到上为正方向。在实际应用中,不同的Graphics Context可能使用了不同的坐标系统,它们在Quartz 2D默认的坐标系统基础之上做了调整,来适应不同的场景。

iOS中的视图绘制

在iOS应用程序中,如果需要在屏幕上进行视图绘制,需要创建一个UIView对象,UIView是定义为在屏幕上的一块矩形区域,用于管理这块区域所呈现的内容,而在这个矩形区域内,我们可以通过重写drawRect:方法来自定义一些需要显示的内容。drawRect:方法在视图显示在屏幕上及它的内容需要更新时被系统自动调用,我们手动调用是无效的,系统提供了两个方法让我们进行间接调用drawRect:来达到重绘视图的目的:

// 方法一:重新绘制这个view
- (void)setNeedsDisplay;
// 方法二:重新绘制view的某个区域
- (void)setNeedsDisplayInRect:(CGRect)rect;

获取Graphics Context

在调用自定义的drawRect:后,视图对象自动配置绘图环境以便代码能立即执行绘图操作。作为配置的一部分,视图对象将为当前的绘图环境创建一个Graphics Context。我们可以在drawRect:中使用代码获取这个context:

CGContextRef context = UIGraphicsGetCurrentContext();

这里需要注意的是,这个context的坐标系是默认原点位于左上角,y轴正方向为向下。这是因为UIKit使用的默认的坐标系统与Quartz 2D默认的坐标系统不同,在UIKit中,默认原点位于左上角,y轴正方向为向下。所以UIView通过修改Quartz的Graphics Context的CTM(ps:一种仿射矩阵,通过平移(translation)、旋转(rotation)、缩放(scale)操作可以将点从一个坐标空间映射到另外一个坐标空间)以使其与UIKit的坐标系匹配。

创建与绘制路径(Path)

路径我们可以理解为我们手中的画笔绘制出来的一个或者多个形状(子路径),每一个形状可以是线、圆、矩形、星形等简单的形状,也可以是一些更复杂的自定义形状。如下图显示了一些路径。左上角的直线可以是虚线;直线也可以是实线。上边中间的路径是由多条曲线组成的开放路径;右上角的同心圆填充了颜色,但没有描边;左下角的加利福尼亚州是闭合路径,由许多曲线和直线构成,且对路径进行填充和描边。


    - (void)drawRect:(CGRect)rect {
    
    // 1.获取Quartz Context
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 2.开始构建路径,设置一个起点的坐标,绘制系统会追踪至该点进行绘制
    // 参数一:关联的绘制目标
    // 参数二:起点的x坐标
    // 参数三:起点的y坐标
    CGContextMoveToPoint(context, 100, 100);
    // 3.将当前点与一个点(这里我们举例为(200,100))之间构建一条直线
    // 参数一:关联的绘制目标
    // 参数二:该端点的x坐标
    // 参数三:该端点的y坐标
    CGContextAddLineToPoint(context, 200, 100);
    
    }

让我们共同分析一下上面的代码:
第一步,我们在drawRect:方法中获取Quartz Context;
第二步,我们准备开始构建路径,在构建路径时我们使用了CGContextMoveToPoint()函数来确定绘制的一个起始点,context会将该点信息存储至默认生成的path中;
第三步,我们使用了CGContextAddLineToPoint()函数将两点之间建立直线关系,同样context会将该点信息也存储至默认生成的path中,注意此时,我们只是构建了一个直线路径,并没有绘制,所以运行后不会有效果。
但是上述代码的可读性并不友好,而且在绘制路径后,系统将清空Quartz Context,我们可能想保留路径,特别是在绘制一些比较复杂场景时,我们需要反复使用,所以我们在实际开发中,通常会手动创建一个path对象,而且这个path对象通常是可变的,使用的函数为CGPathCreateMutable(),数据类型为:CGMutablePathRef,然后我们可以向该对象添加直线、弧、曲线和矩形等。Quartz提供了一个类似于操作图形上下文的CGPath的函数集合。这些路径函数直接操作CGPath对象,而不是Quartz Context。这些函数包括:

CGPathCreateMutable,取代CGContextBeginPath
CGPathMoveToPoint,取代CGContextMoveToPoint
CGPathAddLineToPoint,取代CGContexAddLineToPoint
CGPathAddCurveToPoint,取代CGContexAddCurveToPoint
CGPathAddEllipseInRect,取代CGContexAddEllipseInRect
CGPathAddArc,取代CGContexAddArc
CGPathAddRect,取代CGContexAddRect
CGPathCloseSubpath,取代CGContexClosePath

如果想要添加一个路径或者多个路径到Quartz Context,可以调用CGContextAddPath。路径将保留在Quartz Context中,直到Quartz绘制它。

基本图形绘制

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGMutablePathRef linePath = CGPathCreateMutable();
    // 设置一个起点的坐标,绘制系统会追踪至该点进行绘制
    CGPathMoveToPoint(linePath, NULL, 100, 100);
    // 将当前点与一个点(这里我们举例为(200,100))之间构建一条直线
    CGPathAddLineToPoint(linePath, NULL, 200, 100);
    CGContextAddPath(context, linePath);
    // 设置描边的颜色(两种方式)
    // 方法一:
    // CGContextSetRGBStrokeColor(context, 211.f / 255.f, 106.f / 255.f, 119.f / 255.f, 1.0);
    // 方法二:
    [[UIColor redColor] setStroke];
    // 设置描边的宽度
    CGContextSetLineWidth(context, 10);
    // 绘制路径-仅描边
    CGContextStrokePath(context);
    // 注意内存管理
    CGPathRelease(linePath);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGMutablePathRef linesPath = CGPathCreateMutable();
    CGPoint pointArray[] = {CGPointMake(100, 100), CGPointMake(200, 100), CGPointMake(200, 200), CGPointMake(100, 200)};
    // 通过数组绘制一条多端点的线
    // 参数一:关联的绘制路径
    // 参数二:仿射变化
    // 参数三:数组
    // 参数四:数组元素个数
    CGPathAddLines(linesPath, NULL, pointArray, sizeof(pointArray) / sizeof(CGPoint));
    CGContextAddPath(context, linesPath);
    // 设置填充的颜色
    // 方法一:
    CGContextSetRGBFillColor(context, 211.f / 255.f, 106.f / 255.f, 119.f / 255.f, 1.0);
    // 方法二:
    [[UIColor redColor] setFill];
    // 闭合绘制路径,会从当前点绘制至起点
    CGContextClosePath(context);
    // 绘制路径-仅描边
    //    CGContextStrokePath(context);
    // 绘制路径-仅填充
    //    CGContextFillPath(context);
    // 绘制路径-既有描边也有填充
    CGContextDrawPath(context, kCGPathFillStroke);
    CGPathRelease(linesPath);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGMutablePathRef arcPath = CGPathCreateMutable();
    // 绘制一条弧
    // 参数一:关联的绘制路径
    // 参数二:仿射变化
    // 参数三:原点的坐标x
    // 参数四:原点的坐标y
    // 参数五:半径
    // 参数六:起始弧度
    // 参数七:结束弧度
    // 参数八:绘制方向(0为顺时针,1为逆时针)
    CGPathAddArc(arcPath, NULL, 100, 100, 50, 0, 135 * (M_PI / 180), 1);
    CGContextAddPath(context, arcPath);
    CGContextStrokePath(context);
    CGPathRelease(arcPath);   

二次Bezier曲线是 P0至P1 的连续点Q0和P1至P2 的连续点Q1 组成的线段上的连续点B(t),描述一条抛物线:

三次Bezier曲线:

绘制一条三次Bezier曲线:

CGContextRef context = UIGraphicsGetCurrentContext();
CGMutablePathRef curvePath = CGPathCreateMutable();
CGPathMoveToPoint(curvePath, NULL, 20, 100);
// 绘制贝赛尔曲线
// 参数一:关联的绘制路径
// 参数二:仿射变化
// 参数三:控制点1的x坐标
// 参数四:控制点1的y坐标
// 参数五:控制点2的x坐标
// 参数六:控制点2的y坐标
// 参数七:终点的x坐标
// 参数八:终点的y坐标
CGPathAddCurveToPoint(curvePath, NULL, 50, 50, 80, 100, 130, 100);
CGContextAddPath(context, curvePath);
CGContextStrokePath(context);
CGPathRelease(curvePath);   
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGMutablePathRef rectPath = CGPathCreateMutable();
    // 绘制矩形
    // 参数一:关联的绘制路径
    // 参数二:仿射变化
    // 参数二:绘制位置及大小
    CGPathAddRect(rectPath, NULL, CGRectMake(100, 100, 100, 100));
    
    CGContextAddPath(context, rectPath);
    CGContextStrokePath(context);
    CGPathRelease(rectPath);
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGMutablePathRef ellipsePath = CGPathCreateMutable();
    // 绘制椭圆
    // 参数一:关联的绘制路径
    // 参数二:仿射变化
    // 参数三:绘制的位置及大小
    CGPathAddEllipseInRect(ellipsePath, NULL, CGRectMake(100, 100, 200, 200));
    
    CGContextAddPath(context, ellipsePath);
    [[UIColor redColor] setFill];
    CGContextDrawPath(context, kCGPathFillStroke);
    CGPathRelease(ellipsePath);

参考:《Quartz 2D Programming Guide》

上一篇 下一篇

猜你喜欢

热点阅读