Quartz

需要注意的是在UIImageView的子类中重写drawRect:是非法的,你不能把自己的绘制和UIImageView进行结合。


1. Mac OS X的Darwin核心之上的绘图层,有时候也认为是CoreGraphics。共有两种部分组成Quartz:
2. Quartz Compositor,合成视窗系统,管理和合成幕后视窗影像来建立Mac OS X使用者接口。
3. Quartz 2D,是iOS和Mac OS X环境下的二维绘图引擎。
涉及内容包括:基于路径的绘图,透明度绘图,遮盖,阴影,透明层,颜色管理,防锯齿渲染,生成PDF,以及PDF元数据相关处理。



一、Quartz 2D的简单介绍*
1. Quartz 2D属于Core Graphics(所以大多数相关方法的都是以CG开头),是iOS/Mac OSX 提供的在内核之上的强大的2D绘图引擎,并且这个绘图引擎是设备无关的。也就是说,不用关心设备的大小,设备的分辨率,只要利用Quartz 2D,这些设备相关的会自动处理。

2.Quartz 2D能够提供的强大功能如下:

 透明层(transparency layers) 
阴影 基于path的绘图(path-based drawing)
离屏渲染(offscreen rendering)
复杂的颜色处理(advanced color management)
抗锯齿渲染(anti-aliased rendering)
PDF创建,展示,解析(这部分不在这个系列之中) 配合Core Animation, OpenGL ES,UIKit完成复杂的功能 画板-The Graphics Context
而Quartz 2D的容器就是CGContextRef数据模型。这种数据模型是C的结构体,存储了渲染到屏幕上需要的一切信息。


二、Quartz 2D详解:

Quartz 2D的基本数据类型:
Quartz 2D中的数据类型都是透明的,也就是说用户只需要使用即可,不需要实际访问其中的变量。具体的数据类型包括

1. CGPathRef 路径类型,用来绘制路径(注意带有ref后缀的一般都是绘制的画板)
2. CGImageRef,绘制bitmap
3. CGLayerRef,绘制layer,layer可复用,可离屏渲染
4. CGPatternRef,重复绘制
5. CGShadingRef和CGGradientRef,绘制渐变(例如颜色渐变)
6. CGFunctionRef,定义回调函数,CGShadingRef和CGGradientRef的辅助类型
7. CGColorRef and CGColorSpaceRef,定义如何处理颜色
8. CGFontRef,绘制文字


Quartz 2D的坐标

UIKit默认的坐标系统与Quartz不同。在UIKit中,原点位于左上角,y轴正方向为向下。UIView通过将修改Quartz的Graphics Context的CTM[原点平移到左下角,同时将y轴反转(y值乘以-1)]以使其与UIView匹配。这些都是系统自动帮我们完成。


三、直线/矩形

  1. 基本图形绘制需要的属性
1.获取当前上下文(context)(UIGraphicsGetCurrentContext)
2.设置颜色:
CGContextSetFillColorWithColor:设置描边颜色
CGContextSetFillColorWithColor:设置填充颜色
CGContextStrokeRect:描边的范围
CGContextFillRect:填充的范围
4.CGContextSetLineWidth:线宽
5.CGContextSetLineCap:线顶端的样式
6.CGContextSetLineJoin:线拐角的样式
CGContextMoveToPoint:起点
CGContextAddLineToPoint:终点
8.CGContextFillPath :填充的路径
9.CGContextStrokePath:描边的路径


直线、矩形 demo

- (void)drawRect:(CGRect)rect {    
//1.获得当前context
CGContextRef context = UIGraphicsGetCurrentContext();
//设置颜色 (填充色和 描边的颜色)
CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:0.8 alpha:1].CGColor);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
//设置描边线宽
CGContextSetLineWidth(context, 20);
//对矩形进行填色 或 描边
//(注意:如果先描边再填充,由于矩形大小一致,那么描边的线就会被填充的矩形挡住)
CGContextFillRect(context, rect);
CGContextStrokeRect(context, rect);
//-----------------------------------------------------------------

//MARK: ------ 实际line和point的代码
// 设置描边颜色
CGContextSetStrokeColorWithColor(context, [UIColor yellowColor].CGColor);
CGContextSetLineWidth(context, 8.0);//线的宽度
CGContextSetLineCap(context, kCGLineCapRound);//线的顶端
CGContextSetLineJoin(context, kCGLineJoinRound);//线相交的模式

//-----------------------------------------------------------------
//MARK:黄色的 ">" 图形
//移动画笔到哪个点
CGContextMoveToPoint(context,20,20);
//画笔画到哪个点
CGContextAddLineToPoint(context, rect.size.width - 20, rect.size.height / 2 - 20);
CGContextAddLineToPoint(context, 20, rect.size.height - 20);
//根据上下文中的点,成线进行描边
CGContextStrokePath(context);
//------------------------------------------------------------------
//MARK: 红色的小的三角的填充
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextMoveToPoint(context, 0, rect.size.height / 2 - 30);
CGContextAddLineToPoint(context, 30, rect.size.height / 2);
CGContextAddLineToPoint(context, 0, rect.size.height / 2 + 30);
CGContextFillPath(context);
//虚线效果
//CGContextSetLineDash(context, 1, lengths, 1);
//------------------------------------------------------------------
//MARK: 红色虚线效果
CGContextSetStrokeColorWithColor(context,[UIColor redColor].CGColor);
CGContextSetLineWidth(context, 1);
CGContextMoveToPoint(context, rect.size.width - 20, 20);
CGContextAddLineToPoint(context, rect.size.height - 20, rect.size.width - 20);
CGFloat lengths[] = {20};
CGContextSetLineDash(context, 1, lengths, 1);
CGContextStrokePath(context);
}


运行之后的效果:

iOS Quartz 2D绘图知识详解_数组

虚线效果


CGContextSetLineDash参数详解

void CGContextSetLineDash (
CGContextRef _Nullable c,
CGFloat phase,
const CGFloat * _Nullable lengths,
size_t count
);
c 绘制的context,这个不用多说phase,第一个虚线段从哪里开始,例如传入3,则从第三个单位开始 lengths,一个C数组,表示绘制部分和空白部分的分配。例如传入[2,2],则绘制2个单位, 然后空白两个单位,以此重复count lengths的数量




四、曲线— 圆弧的绘制

Quartz提供了两个方法来绘制圆弧


  1. CGContextAddArc,普通的圆弧一部分(以某圆心,某半径,某弧度的圆弧)
  2. CGContextAddArcToPoint,用来绘制圆角
  3. CGContextAddArc

1.结构:


CGContextAddArc (

CGContextRef _Nullable c,
CGFloat x, // 圆心X坐标
CGFloat y, // 圆心Y坐标
CGFloat radius, // 弧度半径
CGFloat startAngle, // 开始的弧度
CGFloat endAngle, // 结束的弧度
int clockwise //1表示顺时针,0表示逆时针 
);


- (void)drawRect:(CGRect)rect {
//--------------------------------------------------------------------
//MARK: 画弧
//1.获取图片上下文
CGContextRef context = UIGraphicsGetCurrentContext();
//2.设置弧度及位置
//根据中心点,半径,起始的弧度,最后的弧度,是否顺时针画一个圆弧
CGContextAddArc(context, rect.size.width / 2, rect.size.height / 2, 20, M_PI_4, M_PI, 1);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
//3.画
CGContextDrawPath(context, kCGPathStroke);
// -----------------------------------------------------
//MARK:画有线圈的圆饼
CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);//设置线的颜色
CGContextSetRGBFillColor(context, 0, 0, 1, 1);//设置填充颜色
CGContextSetLineWidth(context, 2); //设置线的宽度
CGContextAddEllipseInRect(context, CGRectMake(10, 30, 60, 60)); //画一个椭圆或者圆
CGContextDrawPath(context, kCGPathFillStroke);
}

iOS Quartz 2D绘图知识详解_ico_02

2.CGContextAddArcToPoint


void CGContextAddArcToPoint (

CGContextRef _Nullable c,
CGFloat x1,
CGFloat y1,
CGFloat x2,
CGFloat y2,
CGFloat radius
);
c context x1,y1和当前点(x0,y0)决定了第一条切线(x0,y0)->(x1,y1) x2,y2和(x1,y1)决定了第二条切线 radius,想切的半径。
也就是说,绘制一个半径为radius的圆弧,和上述 两条直线都相切。



- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);//设置线的颜色
CGContextSetRGBFillColor(ctx, 0, 0, 1, 1);//设置填充颜色
CGContextSetLineWidth(ctx, 2); //设置线的宽度
//CGContextAddArcToPoint 先要确定三个点,
//1.从哪里开始划线 CGContextMoveToPoint (也就是两条线的交点)
//2.第二个点与起始点 确定一条直线
//3.第三个点与第二个点 确定另外一条直线

//画一个圆角矩形
//确定矩形的位置和大小
CGRect rrect = CGRectMake(rect.size.width / 2 - 30, rect.size.height / 2 - 30, 60.0, 60.0);
CGFloat radius = 15.0;//半径,半径为正方形一半时,那就可以切成圆形

CGFloat
minx = CGRectGetMinX(rrect),//矩形中最小的x
midx = CGRectGetMidX(rrect),//矩形中最大x值的一半
maxx = CGRectGetMaxX(rrect);//矩形中最大的x值

CGFloat
miny = CGRectGetMinY(rrect),//矩形中最小的Y值
midy = CGRectGetMidY(rrect),//矩形中最大Y值的一半
maxy = CGRectGetMaxY(rrect);//矩形中最大的Y值

CGContextMoveToPoint(ctx, minx, midy);//从点A 开始
//从点A到点B再从点B到点C形成夹角进行切圆
CGContextAddArcToPoint(ctx, minx, miny, midx, miny, radius);
CGContextAddArcToPoint(ctx, maxx, miny, maxx, midy, radius);
CGContextAddArcToPoint(ctx, maxx, maxy, midx, maxy, radius);
CGContextAddArcToPoint(ctx, minx, maxy, minx, midy, radius);
CGContextClosePath(ctx);
CGContextDrawPath(ctx, kCGPathFillStroke);
//如果想要进行裁切的话去掉CGContextDrawPath(ctx, kCGPathFillStroke);方法 添加以下方法
//CGContextClip(context);
//CGContextFillPath(context);
//添加图片//CGContextDrawImage(context, rect, self.image.CGImage);
//或者[self.image drawInRect:rect];
}


运行效果:

iOS Quartz 2D绘图知识详解_2d_03


贝塞尔曲线:

- (void)drawRect:(CGRect)rect {    
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineWidth(context, 4);

CGFloat
minx = CGRectGetMinX(rect),//矩形中最小的x
midx = CGRectGetMidX(rect),//矩形中最大x值的一半
maxx = CGRectGetMaxX(rect);//矩形中最大的x值

CGFloat
miny = CGRectGetMinY(rect),//矩形中最小的Y值
midy = CGRectGetMidY(rect),//矩形中最大Y值的一半
maxy = CGRectGetMaxY(rect);//矩形中最大的Y值

//贝塞尔曲线一,两个控制点 红色
CGPoint s = CGPointMake(minx + 10, miny + 10); //起始点
CGPoint e = CGPointMake(maxx - 10, maxy - 10);//终点
CGPoint cp1 = CGPointMake(miny, midy);//控制点1
CGPoint cp2 = CGPointMake(midy, minx);//控制点2
CGContextMoveToPoint(context, s.x, s.y);
CGContextAddCurveToPoint(context, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
CGContextStrokePath(context);
//贝塞尔曲线二,一个控制点 蓝色
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
s = CGPointMake(minx, maxy);
e = CGPointMake(maxx, maxy);
cp1 = CGPointMake(midx, midy);
CGContextMoveToPoint(context, s.x, s.y);
CGContextAddQuadCurveToPoint(context, cp1.x, cp1.y, e.x, e.y);
CGContextStrokePath(context);
}


运行效果:

iOS Quartz 2D绘图知识详解_数组_04


五、颜色渐变

demo

- (void)drawRect:(CGRect)rect {    // Drawing code

// 创建Quartz上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 创建色彩空间对象
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
// 创建起点颜色
CGColorRef beginColor = CGColorCreate(colorSpaceRef, (CGFloat[]){0.01f, 0.5f, 0.01f, 1.0f});
// 创建终点颜色
CGColorRef endColor = CGColorCreate(colorSpaceRef, (CGFloat[]){0.99f, 0.99f, 0.01f, 1.0f});
// 创建颜色数组
const void **values = (const void*[]){beginColor, endColor};//颜色数组
CFArrayRef colorArray = CFArrayCreate(
kCFAllocatorDefault,
values,//颜色数组
2,//数组的个数
nil// CGGradientCreateWithColors的最后一个locations参数可以传空,这样默认为从0.0到1.0。
);
// 创建渐变对象
CGGradientRef gradientRef = CGGradientCreateWithColors(colorSpaceRef, colorArray, (CGFloat[]){
0.0f, // 对应起点颜色位置
1.0f // 对应终点颜色位置
});
// 释放颜色数组
CFRelease(colorArray);
// 释放起点和终点颜色
CGColorRelease(beginColor);
CGColorRelease(endColor);
// 释放色彩空间
CGColorSpaceRelease(colorSpaceRef);
/*
1.context 上线文
2.gradientRef 颜色数组
3.startPoint 开始位置
4.endPoint 结束位置
5.CGGradientDrawingOptions
当你的起点或者终点不在图形上下文的边缘内时,指定该如何处理。你可以使用你的开始或结束颜色来填充渐变以外的空间。此参数为以下值之一:
KCGGradientDrawsAfterEndLocation扩展整个渐变到渐变的终点之后的所有点
KCGGradientDrawsBeforeStartLocation扩展整个渐变到渐变的起点之前的所有点。
0不扩展该渐变。
*/
CGPoint startPoint = CGPointMake(0.0f, 0.0f);
CGPoint endPoint = CGPointMake(rect.size.width, rect.size.height);
CGGradientDrawingOptions options = kCGGradientDrawsAfterEndLocation;
CGContextDrawLinearGradient(
context,
gradientRef,
startPoint,
endPoint,
kCGGradientDrawsBeforeStartLocation
// kCGGradientDrawsAfterEndLocation
);

// 释放渐变对象
CGGradientRelease(gradientRef);
}


效果图:
iOS Quartz 2D绘图知识详解_2d_05


六、多中颜色的渲染
demo

- (void)drawRect:(CGRect)rect {    // 创建Quartz上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 创建色彩空间对象
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
// 创建渐变对象
CGGradientRef gradientRef = CGGradientCreateWithColorComponents(colorSpaceRef,
(CGFloat[]){
1.0f,0.8f,0.5f,1.0f,//第一个颜色RGB 和透明度
0.6f,0.5f,0.6f,1.0f,//第二个颜色RGB 和透明度
0.3f,0.2f,0.f,1.0f,//第三个颜色RGB 和透明度
.0f,0.0f,0.3f,1.0f
},
(CGFloat[]){ 0.0f,0.3f,.6f,1},//颜色渐变的位置
4);//颜色的个数

// 释放色彩空间
CGColorSpaceRelease(colorSpaceRef);
// 填充渐变色
CGContextDrawLinearGradient(context, gradientRef, CGPointMake(0.0f, 0.0f), CGPointMake(320.0f, 460.0f), 0);
// 释放渐变对象
CGGradientRelease(gradientRef);
}


效果:
iOS Quartz 2D绘图知识详解_2d_06


此外还可以绘制文字

//绘制文字(不用开启图形上下文)
NSString *str = @“hehe呵呵heheCGRect wordRect = CGRectMake(100, 100, 100, 40);
//给要显示的文字设置一个范围
//NSForegroundColorAttributeName:文字颜色
//NSFontAttributeName : 字体
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
//设置字体颜色
dict[NSForegroundColorAttributeName] = [UIColor greenColor];
//设置字体大小
dict[NSFontAttributeName] = [UIFont systemFontOfSize:20];
[str drawInRect:wordRect withAttributes:dict];


还可以截屏操作

// 1.开启上下文
UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, 0.0);
// 2.将控制器view的layer渲染到上下文
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
// 3.取出图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 4.结束上下文
UIGraphicsEndImageContext();


iOS Quartz 2D绘图知识详解_ico_07