前言
我们在前面,首先进行了针对 iOS中的多媒体技术相关几个框架概述:
- 进而 用 两篇文章 对 其中的
UIKit
相关要点 进行了分述:
- 然后我们 针对 Core Animation框架的要点 进一步展开分述:
- 我们 在此篇文章 ,将 针对 2D绘图引擎Core Graphics框架的要点 进一步展开分述:
一、Core Graphics 简介
Core Graphics(Quartz 2D)是iOS和macOS平台上的2D绘图引擎
,它是一套C-based API,用于实现图形绘制
、图像处理
、文本渲染
、自定义UI控件
等功能。
Core Graphics
在开发中比较常用的是截屏
/裁剪
/自定义UI控件
,Core Graphics
在iOS开发中的主要价值是自定义UI控件‘
-
Core Graphics
(使用Quartz 2D引擎)
- 当开发者需要在
运行时创建图像
时,可以使用 Core Graphics 去绘制,运行时实时计算
、绘制一系列图像帧
来实现动画。
- 与之相对的是
运行前
创建图像(例如从磁盘中或内存中已经创建好的UIImage图像)
-
Quartz 2D
- Quartz 2D是Core Graphics中的2D 绘制呈现引擎。
- Quartz 2D能够与所有的图形和动画技术(如
Core Animation
, OpenGL ES
, 和 UIKit
等)一起使用。Quartz 2D采用paint模式进行绘制。
1. 几个重要概念
1.1 Page(画布)
Quartz 2D在图像中使用了绘画者模型(painter’s model)。在绘画者模型中,简单说,当前的图层A,在上面添加图层B,A被B遮盖的地方将不会显示出来,而只显示A未被覆盖的地方和B层。
Page可以是一张纸(如果输出设备是打印机),也可以是虚拟的纸张(如果输出设备是PDF文件),还可以是bitmap图像。这根据实际使用的graphics context
而定。
1.2 Path(路径)
Path(路径)可以理解成🖌画笔,我们在画布上绘制,是通过Path来处理的。在UIKit中,有一个UIBezierPath
也是专门用于绘制各种图像的。
- 路径是由直线、曲线、圆弧等基本形状组成的图形对象。
- 可以使用路径来描述和绘制各种图形,包括简单的几何图形和复杂的曲线图形。
1.3 Graphics Context(图形上下文)
图形上下文(CGContext)是一种不透明数据类型,我们不需要关心它的实现以及它是怎么绘制的,只需把我们要绘制的内容告诉它即可。
- Core Graphics使用图形上下文(Graphics Context)来管理绘图环境的状态和属性。
- 通过设置
图形上下文
的属性来控制绘图效果,如颜色
、线宽
、填充模式
等
- Quartz提供了以下几种类型的Graphics Context绘图上下文,详细的介绍将在后续章节说明:
-
位图图形上下文(Bitmap Graphics Context)
位图图形上下文用于将RGB图像,GMYK图像或者黑白图像绘制到一个位图(bitmap)对象中。
-
PDF图形上下文(PDF Graphics Context)
PDF图形上下文可以帮助开发者创建PDF文件,将内容绘制进PDF文件中,其与位图上下文最大的区别在于PDF数据可以保存多页图像。
-
窗口上下文(Window Graphics Context)
用于OS系统中的窗口绘制。
-
图层上下文(Layer Context)
用于将内容绘制在Layer图层上。
-
打印上下文(Post Graphics Context)
使用Mac打印功能时,此上下文用于将内容绘制在打印输出源上。
- 在UIView中,系统会默认创建一个Layer Graphics Context,它对应UIView的layer属性
- 该图形上下文可以在drawRect:方法中获取,开发者只能获取,不能自己重新创建
- 在该图层上下文中绘制的图形,最终会通过CALayer显示出来
- 因此,View之所以能显示东西,完全是因为它内部的layer
获取当前上下文
- 在iOS开发中,视图显示在屏幕上及它的内容需要更新时会被调用drawRect:方法。
- 我们只需要实现drawRect:方法,并且把我们需要绘制的内容提供给获取到的上下文就可以实现绘制。
- (void)drawRect:(CGRect)rect {
// 获取当前上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
...
}
设置绘图的上下文(context)
-
UIGraphicsPushContext(context) :把context压入栈中,并把context设置为当前绘图上下文
-
UIGraphicsPopContext() :将栈顶的上下文弹出,恢复先前的上下文,但是绘图状态不变
- (void)drawRect:(CGRect)rect {
[[UIColor redColor] setFill];
UIGraphicsPushContext(UIGraphicsGetCurrentContext());
[[UIColor blackColor] setFill];
UIGraphicsPopContext();
UIRectFill(CGRectMake(100, 100, 100, 100)); // black color
}
设置绘图的状态(state)
-
CGContextSaveGState(context) :保存context的状态
-
CGContextRestoreGState:恢复保存context的状态
- (void)drawRect:(CGRect)rect {
[[UIColor redColor] setFill];
CGContextSaveGState(UIGraphicsGetCurrentContext());
[[UIColor blackColor] setFill];
CGContextRestoreGState(UIGraphicsGetCurrentContext());
UIRectFill(CGRectMake(100, 100, 100, 100)); // red color
}
1.4 坐标系
- Core Graphics使用笛卡尔坐标系,原点在左下角,x轴向右延伸,y轴向上延伸。
- 开发者可以通过坐标变换来实现坐标系的转换和调整,实现复杂的绘图效果和布局。
UIKit的坐标与Core Graphics的坐标是不一样的,UIKit的坐标默认原点在左上角,而Core Graphics的原点在左下角

通常两个坐标系之间是需要转换的,左图是修改前,右图是修改后: 
那为什么我们在drawRect方法中使用CoreGraphics方法绘制内容的时候可以使用UIKit的坐标系?
因为iOS系统在drawRect返回CGContext的时候,默认帮我们进行了一次变换,以方便开发者直接用UIKit坐标系进行渲染。
转换坐标的API
/*
平移坐标系统。该方法相当于把原来位于 (0, 0) 位置的坐标原点平移到 (tx, ty) 点。
在平移后的坐标系统上绘制图形时,所有坐标点的 X 坐标都相当于增加了 tx,
所有点的 Y 坐标都相当于增加了 ty。
*/
CGContextTranslateCTM(CGContextRef _Nullable c, CGFloat tx, CGFloat ty)
/*
缩放坐标系统。该方法控制坐标系统水平方向上缩放 sx,垂直方向上缩放 sy。
在缩放后的坐标系统上绘制图形时,所有点的 X 坐标都相当于乘以 sx 因子,
所有点的 Y 坐标都相当于乘以 sy 因子。
*/
CGContextScaleCTM(CGContextRef _Nullable c, CGFloat sx, CGFloat sy)
/*
旋转坐标系统。该方法控制坐标系统旋转 angle 弧度。
在缩放后的坐标系统上绘制图形时,所有坐标点的 X、Y 坐标都相当于旋转了 angle弧度之后的坐标。
*/
CGContextRotateCTM(CGContextRef _Nullable c, CGFloat angle)
2. 功能概述
2.1. 绘图功能:
- Core Graphics提供了丰富的绘图功能,包括:
- 绘制形状(
直线
、矩形
、椭圆
等)
- 绘制路径(
自定义路径
、曲线路径
等)
- 绘制图像(
位图图像
、矢量图像
等)
- ...
- 开发者可以在不同类型的上下文中进行绘制操作,创建自定义的绘图效果,实现各种绘图需求
2.2. 图像处理:
- Core Graphics支持图像的
加载
、绘制
、裁剪
、变换
、合成
、渲染
等操作 实现图像的特效和优化
2.3. 文本渲染:
- Core Graphics提供了文本渲染功能,可以渲染
文本内容
,可以控制文本样式
、排版布局
等实现文本的自定义渲染。
2.4. 颜色和渲染:
- Core Graphics支持颜色管理和渲染操作,可以设置
填充颜色
、描边颜色
、渐变色
等。
2.5. 图形变换:
- Core Graphics提供了图形变换的功能,可以实现
平移
、旋转
、缩放
等变换操作。
2.6. 性能优化:
- Core Graphics利用GPU硬件加速来实现图形渲染和处理,提高绘图效率和性能。
- 开发者可以通过合理使用Core Graphics的API和功能来优化绘图性能,实现流畅的图形绘制和处理
3. 核心类介绍
Core Graphics(Quartz 2D)框架中的核心类主要包括以下几个:
3.1. CGContext:
- CGContext是Core Graphics中的
绘图上下文
,用于执行绘图操作和渲染图形。
- CGContext提供了绘制路径、图像、文本等的功能,是实现图形绘制的核心类。
CGContextAPI简介:
-
CGContextSaveGState:压栈操作,
保存
一份当前图形上下文
-
CGContextRestoreGState:出栈操作,
恢复
一份当前图形上下文
3.1.1 图形上下文的坐标空间变换
CTM(current transformation matrix当前转换矩阵)
-
CGContextScaleCTM:坐标系X,Y缩放
-
CGContextTranslateCTM:坐标系平移
-
CGContextRotateCTM:坐标系旋转
-
CGContextConcatCTM:
-
CGContextGetCTM:获得一份CTM
3.1.2 设置Line属性及连接样式
-
CGContextSetLineWidth: 设置线宽
-
CGContextSetLineCap: 用于设置线条的端点样式
-
CGContextSetLineJoin: 用于设置路径的线条连接样式
-
CGContextSetMiterLimit:设置斜角连接的限制比例
-
CGContextSetLineDash: 设置虚线样式
-
CGContextSetFlatness: 设置线条的平直度
-
CGContextSetAlpha: 设置透明度
CGContextSetAlpha(context, 0.2)
-
CGContextSetBlendMode:设置图形上下文的混合模式
3.1.3 设置Path绘制
-
CGContextBeginPath:告诉图形上下文开始新的路径,以便后续的路径操作可以添加到这个新路径中
-
CGContextMoveToPoint:画笔移动到该点开始画线
-
CGContextAddLineToPoint:画直线到该点
-
CGContextAddCurveToPoint:画三次曲线函数
CGContextMoveToPoint(context, 200, 300);//设置Path的起点
CGContextAddCurveToPoint(context,250, 280, 250, 400, 280, 300);//设置贝塞尔曲线的控制点坐标和控制点坐标终点坐标
CGContextStrokePath(context);
-
CGContextAddQuadCurveToPoint:画二次曲线
CGContextMoveToPoint(context, 120, 300);//设置Path的起点
CGContextAddQuadCurveToPoint(context,190, 310, 120, 390);//设置贝塞尔曲线的控制点坐标和终点坐标
CGContextStrokePath(context);
-
CGContextClosePath:闭合曲线
-
CGContextAddRect:画矩形
CGContextAddRect(context,CGRectMake(140, 120, 60, 30));//画方框
-
CGContextAddRects:
-
CGContextAddLines:
/*画三角形*/
//只要三个点就行跟画一条线方式一样,把三点连接起来
CGPoint sPoints[3];//坐标点
sPoints[0] =CGPointMake(100, 220);//坐标1
sPoints[1] =CGPointMake(130, 220);//坐标2
sPoints[2] =CGPointMake(130, 160);//坐标3
CGContextAddLines(context, sPoints, 3);//添加线
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke); //根据坐标绘制路径
-
CGContextAddEllipseInRect:
//画椭圆
CGContextAddEllipseInRect(context, CGRectMake(160, 180, 20, 8)); //椭圆
CGContextDrawPath(context, kCGPathFillStroke);
-
CGContextAddArc:
-
CGContextAddArcToPoint:
/*画圆角矩形*/
float fw = 180;
float fh = 280;
CGContextMoveToPoint(context, fw, fh-20); // 开始坐标右边开始
CGContextAddArcToPoint(context, fw, fh, fw-20, fh, 10); // 右下角角度
CGContextAddArcToPoint(context, 120, fh, 120, fh-20, 10); // 左下角角度
CGContextAddArcToPoint(context, 120, 250, fw-20, 250, 10); // 左上角
CGContextAddArcToPoint(context, fw, 250, fw, fh-20, 10); // 右上角
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke); //根据坐标绘制路径
-
CGContextAddPath:
-
CGContextCopyPath:
3.1.4 获取路径信息
-
CGContextReplacePathWithStrokedPath:
-
CGContextIsPathEmpty:表示目前的图形上下文是否包含任何的子路径。
bool isPathEmpty = CGContextIsPathEmpty(ctf);
-
CGContextGetPathCurrentPoint:返回一个非空的路径中的当前点。
CGPoint currentPoint = CGContextGetPathCurrentPoint(ctf);// 获取当前画笔处于最后的那一个点
-
CGContextGetPathBoundingBox:返回包含当前路径的最小矩形。
CGRect boxRect = CGContextGetPathBoundingBox(ctf);// 包含路径的最小矩形
-
CGContextPathContainsPoint:检查当前路径中是否包含指定的点。
3.1.5 路径绘制
-
CGContextStrokePath:
-
CGContextDrawPath:
两者区别:
/*CGPathDrawingMode是填充方式,枚举类型
kCGPathFill:只有填充(非零缠绕数填充),不绘制边框
kCGPathEOFill:奇偶规则填充(多条路径交叉时,奇数交叉填充,偶交叉不填充)
kCGPathStroke:只有边框
kCGPathFillStroke:既有边框又有填充
kCGPathEOFillStroke:奇偶填充并绘制边框
*/
CGContextStrokePath(context); 直接在图形上下文中渲染路径
CGContextDrawPath(context, kCGPathFillStroke); //指定模式下渲染路径
/*画矩形*/
CGContextStrokeRect(context,CGRectMake(100, 120, 10, 10));//画方框
CGContextFillRect(context,CGRectMake(120, 120, 10, 10));//填充框
-
CGContextFillRects:
-
CGContextStrokeRectWithWidth:
-
CGContextClearRect:
-
CGContextFillEllipseInRect:
-
CGContextStrokeEllipseInRect:
-
CGContextStrokeLineSegments:
3.1.6 修改剪裁路径
-
CGContextClip:
-
CGContextEOClip:
-
CGContextClipToMask:
-
CGContextGetClipBoundingBox:
-
CGContextClipToRect:
-
CGContextClipToRects:
3.1.7 设置颜色、色彩空间及阴影值
-
Quartz 中的颜色是用一组数值来表示。而颜色空间用于解析这些颜色信息,常用颜色空间有 RGB 、CMYK等。
-
Quartz 支持通用颜色空间、设备独立颜色空间、设备依赖颜色空间、索引颜色空间和模式(Pattern)颜色空间。
-
iOS不支持设备独立颜色空间和通用颜色空间。iOS应用程序必须使用设备颜色空间。
-
CGContextSetFillColorWithColor://设置填充颜色
-
CGContextSetStrokeColorWithColor://设置描边颜色
-
调用如下函数来便捷的使用 CGColor 设置颜色值并使用 CGColor 指定的颜色空间
//设置填充颜色
UIColor *aColor = [UIColor blueColor];//blue蓝色
CGContextSetFillColorWithColor(context, aColor.CGColor);//填充颜色
//设置描边颜色
aColor = [UIColor yellowColor];
CGContextSetStrokeColorWithColor(context, aColor.CGColor);//线框颜色
3.1.8 创建设备依赖颜色空间
Quartz 中的颜色是用一组数值来表示。而颜色空间用于解析这些颜色信息,常用颜色空间有 RGB 、CMYK等。
Quartz 支持通用颜色空间、设备独立颜色空间、设备依赖颜色空间、索引颜色空间和模式(Pattern)颜色空间。
iOS不支持设备独立颜色空间和通用颜色空间。iOS应用程序必须使用设备颜色空间。
-
CGColorSpaceCreateDeviceGray() 创建设备依赖灰度颜色空间。
-
CGColorSpaceCreateDeviceRGB() 创建设备依赖RGB颜色空间。
-
CGColorSpaceCreateDeviceCMYK() 创建设备依赖CMYK颜色
空间。
-
CGContextSetFillColorSpace:
-
CGContextSetStrokeColorSpace:
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextSetFillColorSpace(context, colorSpace);
CGContextSetStrokeColorSpace(context, colorSpace);
-
CGContextSetFillColor:
-
CGContextSetStrokeColor:
//Device RGB.
CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
-
CGContextSetGrayFillColor:
-
CGContextSetGrayStrokeColor:
-
调用如下函数来便捷的设置设备依赖CMYK颜色空间并设置颜色值。
//Device Gray.
CGContextSetGrayStrokeColor(context, 0.5, 1);
CGContextSetGrayFillColor(context, 0.5, 1);
-
CGContextSetRGBFillColor:
-
CGContextSetRGBStrokeColor:
CGContextSetRGBStrokeColor (context, 142.0/ 255.0, 161.0/ 255.0, 189.0/ 255.0, 1.0);
-
CGContextSetCMYKFillColor:
-
CGContextSetCMYKStrokeColor:
-
调用如下函数来便捷的设置设备依赖CMYK颜色空间并设置颜色值。
//Device CMYK.
CGContextSetCMYKStrokeColor(context, 1, 0, 0, 0, 1);
CGContextSetCMYKFillColor(context, 1, 0, 0, 0, 1);
-
CGContextSetRenderingIntent:设置再现意图
每个设备都有固定的可复制的颜色范围(gamut),这是设备的物理性质决定的。当图像从一个颜色空间向另一个颜色空间转换时,有些源设备颜色空间中呈现的颜色,不能在目标设备颜色空间中复制出来,这些不能复制的颜色叫色域外(out-of-gamut)颜色。比如 RGB 颜色空间比 CMYK 的颜色空间要大,有些在显示器上能显示的颜色不能在打印机上同样打印出来。因为我们不能在目标设备颜色空间中复制出色域外颜色,我们必须用一些其他颜色来替代他们。颜色空间转换时颜色替换调整的规则就是再现意图。更详细的说明可以查看 这里 和 这里
- 再现意图用于指定如何将源颜色空间的颜色映射到图形上下文的目标颜色空间的颜色范围内。
- 如果不显式的指定再现意图,Quartz 使用“相对色度再现意图”应用于所有绘制(不包含位图图像)。
- 对于位图图像,Quartz默认使用“感知再现意图”。
- 调用 CGContextSetRenderingIntent(context, kCGRenderingIntentDefault) 来设置再现意图。
- 再现意图共有以下 5 种
typedef CF_ENUM (int32_t, CGColorRenderingIntent) {
kCGRenderingIntentDefault,
kCGRenderingIntentAbsoluteColorimetric,
kCGRenderingIntentRelativeColorimetric,
kCGRenderingIntentPerceptual,
kCGRenderingIntentSaturation
};
1 kCGRenderingIntentDefault:默认再现意图。
2 kCGRenderingIntentAbsoluteColorimetric:绝对色度再现意图。将输出设备颜色域外的颜色映射为输出设备域内与之最接近的颜色。这可以产生一个裁减效果,因为色域外的两个不同的颜色值可能被映射为色域内的同一个颜色值。当图形使用的颜色值同时包含在源色域及目标色域内时,这种方法是最好的。常用于logo或者使用专色(spot color)时。
3 kCGRenderingIntentRelativeColorimetric:相对色度再现意图。转换所有的颜色(包括色域内的),以补偿图形上下文的白点与输出设备白点之间的色差。
4 kCGRenderingIntentPerceptual:感知再现意图。通过压缩图形上下文的色域来适应输出设备的色域,并保持源颜色空间的颜色之间的相对性。感知渲染意图适用于相片及其它复杂的高细度图片。
5 kCGRenderingIntentSaturation:饱和度再现意图。把颜色转换到输出设备色域内时,保持颜色的相对饱和度。结果是包含亮度、饱和度颜色的图片。饱和度意图适用于生成低细度的图片,如描述性图表。
-
CGContextDrawImage:在指定区域画图片
/*图片*/
UIImage *image = [UIImage imageNamed:@"apple.jpg"];
[image drawInRect:CGRectMake(60, 340, 20, 20)];//在坐标中画出图片
// [image drawAtPoint:CGPointMake(100, 340)];//保持图片大小在point点开始画图片,可以把注释去掉看看
CGContextDrawImage(context, CGRectMake(100, 340, 20, 20), image.CGImage);
-
CGContextDrawTiledImage:
-
CGContextGetInterpolationQuality:返回当前的图形上下文的插值(插值(Interpolation)是在不天生像素的环境下增长图像像素大小的一种方法,在周围像素色彩
- 的根蒂根基上用算术公式计算亡失像素的色彩。)质量水平
-
CGContextSetInterpolationQuality:设置图形上下文的插值质量水平。
3.1.9 设置阴影
-
CGContextSetShadowWithColor:
-
CGContextSetShadow:
阴影是如何工作的
Quartz中的阴影是图形状态的一部分。我们可以调用函数CGContextSetShadow来创建,并传入一个图形上下文、偏移值及模糊值。阴影被设置后,任何绘制的对象都有一个阴影,且该阴影在设备RGB颜色空间中呈现出黑色的且alpha值为1/3。换句话说,阴影是用RGBA值{0, 0, 0, 1.0/3.0}设置的。
我们可以调用函数CGContextSetShadowWithColor来设置彩色阴影,并传递一个图形上下文、 偏移值、模糊值有CGColor颜色对象。颜色值依赖于颜色空间。
如何在调用CGContextSetShadow或CGContextSetShadowWithColor之前保存了图形状态,我们可以通过恢复图形状态来关闭阴影。我们也可以通过设置阴影颜色为NULL来关闭阴影
阴影有三个属性:
x偏移值,用于指定阴影相对于图片在水平方向上的偏移值。
y偏移值,用于指定阴影相对于图片在竖直方向上的偏移值。
模糊(blur)值,用于指定图像是有一个硬边
CGContextSetShadow(context, CGSizeMake(10, -20), 10);
3.1.10 绘制渐变色
-
CGContextDrawLinearGradient:
-
CGContextDrawRadialGradient:
//第二种填充方式
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGFloat colors[] =
{
1,1,1, 1.00,
1,1,0, 1.00,
1,0,0, 1.00,
1,0,1, 1.00,
0,1,1, 1.00,
0,1,0, 1.00,
0,0,1, 1.00,
0,0,0, 1.00,
};
CGGradientRef gradient = CGGradientCreateWithColorComponents
(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4));//形成梯形,渐变的效果
CGColorSpaceRelease(rgb);
//画线形成一个矩形
//CGContextSaveGState与CGContextRestoreGState的作用
/*
CGContextSaveGState函数的作用是将当前图形状态推入堆栈。之后,您对图形状态所做的修改会影响随后的描画操作,但不影响存储在堆栈中的拷贝。在修改完成后,您可以通过CGContextRestoreGState函数把堆栈顶部的状态弹出,返回到之前的图形状态。这种推入和弹出的方式是回到之前图形状态的快速方法,避免逐个撤消所有的状态修改;这也是将某些状态(比如裁剪路径)恢复到原有设置的唯一方式。
*/
CGContextSaveGState(context);
CGContextMoveToPoint(context, 220, 90);
CGContextAddLineToPoint(context, 240, 90);
CGContextAddLineToPoint(context, 240, 110);
CGContextAddLineToPoint(context, 220, 110);
CGContextClip(context);//context裁剪路径,后续操作的路径
//CGContextDrawLinearGradient(CGContextRef context,CGGradientRef gradient, CGPoint startPoint, CGPoint endPoint,CGGradientDrawingOptions options)
//gradient渐变颜色,startPoint开始渐变的起始位置,endPoint结束坐标,options开始坐标之前or开始之后开始渐变
CGContextDrawLinearGradient(context, gradient,CGPointMake
(220,90) ,CGPointMake(240,110),
kCGGradientDrawsAfterEndLocation);
CGContextRestoreGState(context);// 恢复到之前的context
//再写一个看看效果
CGContextSaveGState(context);
CGContextMoveToPoint(context, 260, 90);
CGContextAddLineToPoint(context, 280, 90);
CGContextAddLineToPoint(context, 280, 190);
CGContextAddLineToPoint(context, 260, 190);
CGContextClip(context);//裁剪路径
//说白了,开始坐标和结束坐标是控制渐变的方向和形状
CGContextDrawLinearGradient(context, gradient,CGPointMake
(260, 90) ,CGPointMake(260, 190),
kCGGradientDrawsAfterEndLocation);
CGContextRestoreGState(context);// 恢复到之前的context
//下面再看一个颜色渐变的圆
//参数1:图形上下文
//参数2:渐变色
//参数3:开始中心点
//参数4:开始半径
//参数5:结束中心点
//参数6:结束半径
//参数7:渲染模式
CGContextDrawRadialGradient(context, gradient, CGPointMake(400, 100), 0.0, CGPointMake(350, 100), 30, kCGGradientDrawsBeforeStartLocation);
-
CGContextDrawShading:
3.1.11 绘制文本
-
CGContextSetCharacterSpacing:
-
CGContextSetTextPosition:
-
CGContextGetTextPosition:
-
CGContextSetTextMatrix:
-
CGContextGetTextMatrix:
-
CGContextSetTextDrawingMode:
-
CGContextSetFont:
-
CGContextSetFontSize:
-
CGContextShowGlyphsAtPositions:
3.1.12 PDF
** CGContextDrawPDFPage**: 绘制一个PDF页面到当前的用户空间
3.1.13 建立一个基于页面的图形上下文
-
CGContextBeginPage:
-
CGContextEndPage:
3.1.14 管理图形上下文
-
CGContextRetain:
-
CGContextRelease:
-
CGContextSynchronize:
3.1.15 锯齿功能
-
CGContextSetShouldAntialias:设置图形上下文的抗锯齿开启或关闭。
-
CGContextSetAllowsAntialiasing:
3.1.16 字体展示功能
-
CGContextSetShouldSmoothFonts:
-
CGContextSetAllowsFontSmoothing:
-
CGContextSetShouldSubpixelPositionFonts:
-
CGContextSetAllowsFontSubpixelPositioning:
-
CGContextSetShouldSubpixelQuantizeFonts:
-
CGContextSetAllowsFontSubpixelQuantization:
3.1.17 使用透明图层
Quartz的透明层类似于许多流行的图形应用中的层。层是独立的实体。Quartz维护为每个上下文维护一个透明层栈,并且透明层是可以嵌套的。但由于层通常是栈的一部分,所以我们不能单独操作它们。
我们通过调用函数CGContextBeginTransparencyLayer来开始一个透明层,该函数需要两个参数:图形上下文与CFDictionary对象。字典中包含我们所提供的指定层额外信息的选项,但由于Quartz 2D API中没有使用字典,所以我们传递一个NULL。在调用这个函数后,图形状态参数保持不变,除了alpha值[默认设置为1]、阴影[默认关闭]、混合模式[默认设置为normal]、及其它影响最终组合的参数。
在开始透明层操作后,我们可以绘制任何想显示在层上的对象。指定上下文中的绘制操作将被当成一个组合对象绘制到一个透明背景上。这个背景被当作一个独立于图形上下文的目标缓存。
当绘制完成后,我们调用函数CGContextEndTransparencyLayer。Quartz将结合对象放入上下文,并使用上下文的全局alpha值、阴影状态及裁减区域作用于组合对象。
在透明层中绘制需要三步:
- 调用函数CGContextBeginTransparencyLayer
- 在透明层中绘制需要组合的对象
- 调用函数CGContextEndTransparencyLayer
-
CGContextBeginTransparencyLayer:直到相应的调用CGContextEndTransparencyLayer,在指定范围内的所有后续绘制操作组合到一个完全透明的背景(它被视为一个单独的目标缓冲区从上下文)
-
CGContextBeginTransparencyLayerWithRect:
-
CGContextEndTransparencyLayer:
//代码示例:
CGContextBeginTransparencyLayer(context, NULL);
CGFloat wd = 300;
CGFloat ht = 300;
CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
CGContextFillRect(context, CGRectMake (wd/3 + 50, ht/2, wd/4, ht/4));
CGContextEndTransparencyLayer(context);
3.1.18 用户空间与设备空间互换
-
CGContextGetUserSpaceToDeviceSpaceTransform:
-
CGContextConvertPointToDeviceSpace:
-
CGContextConvertPointToUserSpace:
-
CGContextConvertSizeToDeviceSpace:
-
CGContextConvertSizeToUserSpace:
-
CGContextConvertRectToDeviceSpace:
-
CGContextConvertRectToUserSpace:
3.2. CGPath:
- CGPath是Core Graphics中
表示路径
的类,用于创建和管理路径对象。
- CGPath可以包含
直线
、曲线
、矩形
、椭圆
等形状,用于定义绘制的轮廓和形状。
- CGPath能够被填充和stroke
常用API
在Objective-C中,CGPath
是Core Graphics框架中用于创建和管理路径对象的类。下面是一些常用的CGPath
相关的Objective-C API:
-
创建路径:
CGPathRef CGPathCreateMutable(void);
CGPathRef CGPathCreateMutableCopy(CGPathRef path);
CGPathRef CGPathCreateWithRect(CGRect rect, const CGAffineTransform *transform);
CGPathRef CGPathCreateWithEllipseInRect(CGRect rect, const CGAffineTransform *transform);
CGPathRef CGPathCreateWithRoundedRect(CGRect rect, CGFloat cornerWidth, CGFloat cornerHeight, const CGAffineTransform *transform);
-
释放路径:
void CGPathRelease(CGPathRef path);
-
添加路径元素:
void CGPathMoveToPoint(CGMutablePathRef path, const CGAffineTransform *m, CGFloat x, CGFloat y);
void CGPathAddLineToPoint(CGMutablePathRef path, const CGAffineTransform *m, CGFloat x, CGFloat y);
void CGPathAddRect(CGMutablePathRef path, const CGAffineTransform *m, CGRect rect);
void CGPathAddEllipseInRect(CGMutablePathRef path, const CGAffineTransform *m, CGRect rect);
void CGPathAddArc(CGMutablePathRef path, const CGAffineTransform *m, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, bool clockwise);
-
其他操作:
bool CGPathContainsPoint(CGPathRef path, const CGAffineTransform *m, CGPoint point, bool eoFill);
CGRect CGPathGetBoundingBox(CGPathRef path);
bool CGPathEqualToPath(CGPathRef path1, CGPathRef path2);
-
路径属性:
CGPathRef CGPathCreateCopyByTransformingPath(CGPathRef path, const CGAffineTransform *transform);
CGPathRef CGPathCreateCopyByDashingPath(CGPathRef path, const CGAffineTransform *transform, CGFloat phase, const CGFloat *lengths, size_t count);
CGPathRef CGPathCreateCopyByStrokingPath(CGPathRef path, const CGAffineTransform *tran
3.3. CGImage:
- CGImage是Core Graphics中
表示图像
的类,用于加载、创建和处理位图图像。
- CGImage可以从文件、数据或其他来源创建,用于图像的绘制和处理。
常用API
-
创建
CGImage
:
CGImageRef CGImageCreate(size_t width,
size_t height,
size_t bitsPerComponent,
size_t bitsPerPixel,
size_t bytesPerRow,
CGColorSpaceRef space,
CGBitmapInfo bitmapInfo,
CGDataProviderRef provider,
const CGFloat *decode,
bool shouldInterpolate,
CGColorRenderingIntent intent);
-
从UIImage创建CGImage:
CGImageRef CGImageRetain(UIImage *image);
-
释放CGImage:
void CGImageRelease(CGImageRef image);
-
获取CGImage的属性:
size_t CGImageGetWidth(CGImageRef image);
size_t CGImageGetHeight(CGImageRef image);
size_t CGImageGetBitsPerComponent(CGImageRef image);
size_t CGImageGetBitsPerPixel(CGImageRef image);
size_t CGImageGetBytesPerRow(CGImageRef image);
CGColorSpaceRef CGImageGetColorSpace(CGImageRef image);
CGBitmapInfo CGImageGetBitmapInfo(CGImageRef image);
CGDataProviderRef CGImageGetDataProvider(CGImageRef image);
-
绘制CGImage:
void CGContextDrawImage(CGContextRef c, CGRect rect, CGImageRef image);
-
从CGImage创建UIImage:
UIImage *UIImageWithCGImage(CGImageRef cgImage);
-
从CGImage创建CIImage:
CIImage *CIImageWithCGImage(CGImageRef image);
3.4. CGColor:
- CGColor是Core Graphics中
表示颜色
的类,用于定义绘制和填充的颜色。
- CGColor可以表示RGB、RGBA、灰度等颜色空间,用于设置绘制和填充的颜色值。
-
CGColor
和 CGColorSpace
;用来进行颜色和颜色空间管理;
3.5. CGGradient:
- CGGradient是Core Graphics中
表示渐变
的类,用于创建和管理颜色渐变效果。
- CGGradient可以
定义线性渐变
、径向渐变
等效果,用于实现丰富的颜色渐变效果。
-
CGShading
和 CGGradient
:用于绘制剃度;
3.6. CGFont:
- CGFont是Core Text框架中
表示字体
的类,用于处理文本的字体和排版。
- CGFont可以设置字体的
样式
、大小
、粗细
等属性,用于文本的渲染和显示。
3.7 其他常用类:
-
CGLayer
:用来表示一个能够用于重复绘制和offscreen绘制的绘制层;
-
CGPattern
:用来表示Pattern,用于重复绘制;
-
CGPDFContentStream
、CGPDFScanner
、CGPDFPage
、CGPDFObject
,CGPDFStream
, CGPDFString
等用来进行pdf文件的创建、解析和显示
二、CGContext常用功能
在日常开发中,一般绘图都是用UIBezierPath居多,偶尔也会与CGContext配合绘图。因此,此处列举CGContext绘图只是一些简单、常规的使用方法。
1. 绘制曲线
- 获取当前控件的图形上下文
- 描述绘画图形内容
a. 创建图形起始点
b. 添加控制点和终点
- 设置图形上下文状态
- 渲染图形上下文
- (void)drawRect:(CGRect)rect {
// 1. 获取当前控件的图形上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 2. 描述绘画图形内容
// a. 创建图形起始点
CGContextMoveToPoint(context, 0, 0);
// b. 添加控制点和终点,控制点(300,200),终点(0,250)
CGContextAddQuadCurveToPoint(context, 300, 200, 0, 250);
// 3. 设置图形上下文状态
// 设置颜色
[[UIColor redColor] set];
// 设置线宽
CGContextSetLineWidth(context, 10);
// 4. 渲染图形上下文
CGContextStrokePath(context);
}
2. 绘制图形
常见问题:
- 关闭路径closePath:从路径的终点连接到起点
- 填充路径CGContextFillPath:有了封闭的路径就能填充。
- 设置填充颜色 [[UIColor blueColor] setFill];
- 设置描边颜色 [[UIColor redColor] setStroke];
- 不显示描边颜色,为什么?没有设置线宽
- 设置线宽,还是不显示,为什么?因为绘制路径不对。
- 即填充又描边CGContextDrawPath:kCGPathFillStroke。
- 圆的起点在圆右边水平线
(一). 圆角矩形
// RoundedRect: 坐标与宽高
// cornerRadius: 角半径
UIBezierPath *oblongPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 100, 150) cornerRadius:100];
// 颜色
[[UIColor redColor] set];
// 填充
[oblongPath fill];
(二). 扇形
// 扇形
// Center:圆心
// startAngle:弧度
// clockwise:YES:顺时针 NO:逆时针
CGPoint center = CGPointMake(100, 100);
UIBezierPath *sectorPath = [UIBezierPath bezierPathWithArcCenter:center radius:50 startAngle:0 endAngle:M_PI clockwise:YES];
// 从终点连线到圆心
[sectorPath addLineToPoint:center];
// 颜色
[[UIColor blueColor] set];
// // 关闭路径,从终点连线到起始点
// [sectorPath closePath];
// // 描边
// [sectorPath stroke];
// 填充:必须是一个完整的封闭路径,默认就会自动关闭路径
[sectorPath fill];
3. 图片水印
- 开启位图上下文
- 描述绘画内容
a. 绘制图片
b. 绘制文字
c. 绘制图形等
- 从位图上下文获取生成的图片
- 关闭位图上下文
- (void)viewDidLoad {
[super viewDidLoad];
// 创建图片
UIImage *logoImage = [UIImage imageNamed:@"小黄人"];
// 1. 开启位图上下文
// 注意: 位图上下文跟view无关联,所以不需要在drawRect中获取上下文
// size: 位图上下文的尺寸(绘制出新图片的尺寸)
// opaque: 是否透明,YES:不透明 NO:透明,通常设置成透明的上下文
// scale: 缩放上下文,取值0表示不缩放,通常不需要缩放上下文
UIGraphicsBeginImageContextWithOptions(logoImage.size, NO, 0);
// 2. 描述绘画内容
// 绘制原生图片
[logoImage drawAtPoint:CGPointZero];
// 绘制文字
NSString *logo = @"小黄人";
// 创建字典属性
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSForegroundColorAttributeName] = [UIColor redColor];
dict[NSFontAttributeName] = [UIFont systemFontOfSize:20];
[logo drawAtPoint:CGPointMake(51, 27) withAttributes:dict];
// 绘制图形
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(30, 30, 20, 20)];
[[UIColor redColor] set];
[path fill];
// 3. 从上下文获取生成的图片
_logoImage.image = UIGraphicsGetImageFromCurrentImageContext();
// 4. 关闭位图上下文
UIGraphicsEndImageContext();
}
提示:
- 位图上下文跟view无关联,所以不需要在drawRect中获取上下文
- UIGraphicsGetCurrentContext()函数可以获取的不同类型的上下文,CGContextRef变量可以指向不同类型上下文
- 在位图上下文上绘制图形,必须获取位图上下文生成的图片,再显示图片才可以看见绘制的图形
- 位图上下文的获取方式跟layer上下文不一样,位图上下文需要手动创建
4. 图片裁剪
- 开启位图上下文
- 设置裁剪区
- 绘制图片
- 从位图上下文获取生成的图片
- 关闭位图上下文
- (void)viewDidLoad {
[super viewDidLoad];
// 创建图片
UIImage *image = [UIImage imageNamed:@"阿狸头像"];
// 1. 开启上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 2. 设置裁剪区
// 创建图形路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
// 把图形路径设置为裁剪区
[path addClip];
// 3. 绘制图形
[image drawAtPoint:CGPointZero];
// 4. 从位图上下文获取图片
_SJMImage.image = UIGraphicsGetImageFromCurrentImageContext();
// 5. 关闭上下文
UIGraphicsEndImageContext();
}
5. 屏幕截屏
- 开启一个位图上下文
- 获取位图上下文
- 把屏幕上的图层渲染到图形上下文
- 从位图上下文获取图片
- 关闭上下文
- 存储图片
- (void)viewDidLoad {
// 1. 开启一个位图上下文
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);
// 2. 获取位图上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 3. 把屏幕上的图层渲染到图形上下文
[self.view.layer renderInContext:ctx];
// 4. 从位图上下文获取图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 5. 关闭上下文
UIGraphicsEndImageContext();
// 6. 存储图片
// image转data
// compressionQuality:图片质量 1:最高质量
NSData *data = UIImageJPEGRepresentation(image,1);
[data writeToFile:@"/Users/SJM/Desktop/view.png" atomically:YES];
}
三、UIBezierPath绘图
1. UIBezierPath简介
UIBezierPath
这个类在UIKit中, 是Core Graphics框架关于path的一个封装,使用此类可以定义简单的形状,比如我们常用到,矩形
,圆形
,椭圆
,弧
,或者不规则的多边形
贝塞尔曲线的数理知识
1.1 一次贝塞尔曲线
给定点P0,P1一次贝塞尔曲线只是一条两点之间的直线。这条线由下式给出:

一次贝塞尔曲线函数中的t会经由起始点P0至终止点P1的B(t)所描述的曲线。例如当t=0.25时,B(t)即一条由点P0至P1路径的四分之一处。就像由0至1的连续t,B(t)描述一条由P0至P1的直线.

1.2 二次贝塞尔曲线
二次方贝塞尔曲线的路径由给定点P0、P1、P2的函数B(t)追踪:

原理:由 P0 至 P1 的连续点 Q0,描述一条线段。
由 P1 至 P2 的连续点 Q1,描述一条线段。
由 Q0 至 Q1 的连续点 B(t),描述一条二次贝塞尔曲线。
经验:P1-P0为曲线在P0处的切线。

1.3 三次贝塞尔曲线
对于三次曲线,可由线性贝塞尔曲线描述的中介点Q0、Q1、Q2,和由二次曲线描述的点R0、R1所建构:


更多关于贝塞尔曲线的介绍,可参考维基百科的相关介绍
从下面这篇文章可以了解控制点与弧线的关系
1.4 初始化方法
// 初始化方法,需要用实例方法添加线条.使用比较多,可以根据需要任意定制样式,画任何我们想画的图形.
+ (instancetype)bezierPath;
// 返回一个矩形 path
+ (instancetype)bezierPathWithRect:(CGRect)rect;
// 返回一个圆形或者椭圆形 path
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
// 返回一个带圆角的矩形 path ,矩形的四个角都是圆角;
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;
// 返回一个带圆角的矩形 path , UIRectCorner 枚举值可以设置只绘制某个圆角;
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
// 返回一段圆弧,参数说明: center: 弧线中心点的坐标 radius: 弧线所在圆的半径 startAngle: 弧线开始的角度值 endAngle: 弧线结束的角度值 clockwise: 是否顺时针画弧线.
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
// 用一条 CGpath 初始化
+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
// 返回一个反转当前路径的路径对象.(反方向绘制path)
- (UIBezierPath *)bezierPathByReversingPath;
1.5 UIBezierPath 常用属性
1.CGPath
: 将UIBezierPath
类转换成CGPath
2.currentPoint
: 当前path的位置,可以理解为path的终点
3.lineWidth
: 线条宽度
4.lineCapStyle
: 端点样式
5.lineJoinStyle
: 连接类型
6.flatness
: 绘线的精细程度,默认为0.6,数值越大,需要处理的时间越长
7.usesEvenOddFillRule
: 判断奇偶数组的规则绘制图像,图形复杂时填充颜色的一种规则。类似棋盘
8.miterLimit
: 最大斜接长度(只有在使用kCGLineJoinMiter是才有效,最大限制为10), 边角的角度越小,斜接长度就会越大,为了避免斜接长度过长,使用lineLimit属性限制,如果斜接长度超过miterLimit,边角就会以KCALineJoinBevel类型来显示
9.- setLineDash:count:phase:
为path绘制虚线,dash数组存放各段虚线的长度,count是数组元素数量,phase是起始位置
// lineCapStyle // 端点类型
kCGLineCapButt, // 无端点
kCGLineCapRound, // 圆形端点
kCGLineCapSquare // 方形端点(样式上和kCGLineCapButt是一样的,但是比kCGLineCapButt长一点)
// lineJoinStyle // 交叉点的类型
kCGLineJoinMiter, // 尖角衔接
kCGLineJoinRound, // 圆角衔接
kCGLineJoinBevel // 斜角衔接
1.6 UIBezierPath 构建Path
- (void)moveToPoint:(CGPoint)point;
// 以 point点 开始作为起点, 一般用`+ (instancetype)bezierPath`创建的贝塞尔曲线,先用该方法标注一个起点,再调用其他的创建线条的方法来绘制曲线
// 绘制二次贝塞尔曲线的关键方法,即从path的最后一点开始添加一条线到point点
- (void)addLineToPoint:(CGPoint)point;
// 绘制二次贝塞尔曲线的关键方法,和`-moveToPoint:`配合使用. endPoint为终止点,controlPoint为控制点.
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
// 绘制三次贝塞尔曲线的关键方法,以三个点画一段曲线. 一般和moveToPoint:配合使用.
// 其中,起始点由`-moveToPoint:`设置,终止点位为`endPoint:`, 控制点1的坐标controlPoint1,控制点2的坐标是controlPoint2.
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
// 绘制一段圆弧, center:原点坐标,radius:半径,startAngle:起始角度,endAngle:终止角度,clockwise顺时针/逆时针方向绘制
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
// 闭合线
- (void)closePath;
// 移除所有的点,从而有效地删除所有子路径
- (void)removeAllPoints;
// 追加指定的bezierPath到路径上
- (void)appendPath:(UIBezierPath *)bezierPath;
// 用仿射变换矩阵变换路径的所有点
- (void)applyTransform:(CGAffineTransform)transform;
1.7 图形上下文中的路径操作
// 填充路径
- (void)fill;
// 各个点连线
- (void)stroke;
// 填充模式, alpha 设置
// blendMode : https://onevcat.com/2013/04/using-blending-in-ios/
- (void)fillWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
// 链接模式, alpha 设置
- (void)strokeWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;
// 图形绘制超出当前路径范围,则不可见
- (void)addClip;
2. UIBezierPath 基本使用方法
UIBezierPath
对象是CGPathRef数据类型的封装。path如果是基于矢量形状的,都用直线或曲线去创建。我们一般使用UIBezierPath
都是在重写view的drawRect
方法这种情形。我们用直线去创建矩形或多边形,使用曲线创建弧或者圆。创建和使用path对象步骤:
1、 重写View的drawRect
方法
2、 创建UIBezierPath
的对象
3、 使用方法moveToPoint:
设置初始点
4、 根据具体要求使用UIBezierPath
类方法绘图(比如要画线、矩形、圆、弧?等)
5、 设置UIBezierPath
对象相关属性 (比如lineWidth
、lineJoinStyle
、aPath.lineCapStyle
、color
)
6、 使用stroke 或者 fill方法结束绘图
比如我们想要画一条线demo:
- (void)drawRect:(CGRect)rect {
UIColor *color = [UIColor redColor];
[color set]; //设置线条颜色
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 10)];
[path addLineToPoint:CGPointMake(200, 80)];
path.lineWidth = 5.0;
path.lineCapStyle = kCGLineCapRound; //线条拐角
path.lineJoinStyle = kCGLineJoinRound; //终点处理
[path stroke];
}

3. 其他基本使用方法
在介绍其他使用方法之前,我们先来看一下 path
的几个属性,以便下面我进行设置。
- 1、
[color set];
设置线条颜色,也就是相当于画笔颜色
- 2、
path.lineWidth = 5.0;
这个很好理解了,就是划线的宽度
- 3、
path.lineCapStyle
这个线段起点是终点的样式,这个样式有三种:
- 1、
kCGLineCapButt
该属性值指定不绘制端点, 线条结尾处直接结束。这是默认值。
- 2、
kCGLineCapRound
该属性值指定绘制圆形端点, 线条结尾处绘制一个直径为线条宽度的半圆。
- 3、
kCGLineCapSquare
该属性值指定绘制方形端点。 线条结尾处绘制半个边长为线条宽度的正方形。需要说明的是,这种形状的端点与“butt”形状的端点十分相似,只是采用这种形式的端点的线条略长一点而已*
- 4、
path.lineJoinStyle
这个属性是用来设置两条线连结点的样式,同样它也有三种样式供我们选择
- 1、
kCGLineJoinMiter
斜接
- 2、
kCGLineJoinRound
圆滑衔接
- 3、
kCGLineJoinBevel
斜角连接
- 5、
[path stroke];
用 stroke 得到的是不被填充的 view ,[path fill];
用 fill 得到的内部被填充的 view,这点在下面的代码还有绘制得到的图片中有,可以体会一下这两者的不同。
4. 绘制多边形

- 绘制多边形,实际上就是由一些直线条连成,主要使用
moveToPoint:
和addLineToPoint:
方法去创建
-
moveToPoint:
这个方法是设置起始点,意味着从这个点开始
- 我们就可以使用
addLineToPoint:
去设置我们想要创建的多边形经过的点,也就是两线相交的那个点
- 用
addLineToPoint:
去创建一个形状的线段,我们可以连续创建line,每一个line的起点都是先前的终点,终点就是指定的点,将线段连接起来就是我们想要创建的多边形了。
#import "DrawPolygonView.h"
@implementation DrawPolygonView
- (void)drawRect:(CGRect)rect {
UIColor *color = [UIColor redColor];
[color set]; //设置线条颜色
UIBezierPath* path = [UIBezierPath bezierPath];
path.lineWidth = 5.0;
path.lineCapStyle = kCGLineCapRound; //线条拐角
path.lineJoinStyle = kCGLineJoinRound; //终点处理
[path moveToPoint:CGPointMake(200.0, 50.0)];//起点
// Draw the lines
[path addLineToPoint:CGPointMake(300.0, 100.0)];
[path addLineToPoint:CGPointMake(260, 200)];
[path addLineToPoint:CGPointMake(100.0, 200)];
[path addLineToPoint:CGPointMake(100, 70.0)];
[path closePath];//第五条线通过调用closePath方法得到的
// [path stroke];//Draws line 根据坐标点连线
[path fill];//颜色填充
}
- 在这里我们可以看到最后第五条线是用
[path closePath];
得到的
- closePath方法不仅结束一个shape的subpath表述,它也在最后一个点和第一个点之间画一条线段,这个一个便利的方法我们不需要去画最后一条线了。
- 这里我们用到的是
[path fill];//颜色填充
进行坐标连点,但是我们看见的是五边形内部被颜色填充了
- 如果我们使用
[path stroke];
那我们得到的就是一个用线画的五边形。
4.1 添加不规则图形layer,且配置点击事件
我们将上面的代码改一改,添加不规则图形layer,且配置点击事件
- (void)drawRect:(CGRect)rect {
UIColor *color = [UIColor redColor];
[color set]; //设置线条颜色
UIBezierPath* path = [UIBezierPath bezierPath];
path.lineWidth = 3.0;
path.lineCapStyle = kCGLineCapRound; //线条拐角
path.lineJoinStyle = kCGLineJoinRound; //终点处理
[path moveToPoint:CGPointMake(200.0, 50.0)];//起点
// Draw the lines
[path addLineToPoint:CGPointMake(300.0, 100.0)];
[path addLineToPoint:CGPointMake(260, 200)];
[path addLineToPoint:CGPointMake(100.0, 200)];
[path addLineToPoint:CGPointMake(100, 70.0)];
[path closePath];//第五条线通过调用closePath方法得到的
[path stroke];//Draws line 根据坐标点连线
// [path fill];//颜色填充
// 将path 设置为 自定义形状的layer的path.
CGRect boundingRect = path.bounds;
// 获取不规则图形的边界矩形
// 计算边界矩形的中心点坐标
CGPoint center = CGPointMake(CGRectGetMidX(boundingRect), CGRectGetMidY(boundingRect));
CAShapeLayer *layer = [CAShapeLayer layer];
layer.fillColor = [UIColor orangeColor].CGColor;
// 关键点1|设置Path设置形状(不规则图形)
layer.path = path.CGPath;
// 关键点2|设置尺寸
layer.bounds = boundingRect;
// 关键点3|设置位置
layer.position = center;
[self.layer addSublayer:layer];
}
// 点击不规则图形的点击事件处理
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches.allObjects lastObject];
CGPoint loc = [touch locationInView:self];
CALayer *sublayer = [self.layer.sublayers firstObject];
if ([sublayer containsPoint:loc]) {
// if(l == self.diyLayer){
#if DEBUG
NSLog(@"点击了layer1");
#endif
} else {
NSLog(@"点击了其它区域1");
}
}
5. 画矩形或者正方形

- 使用
+ (UIBezierPath *)bezierPathWithRect:(CGRect)rect
这个方法,设置好坐标 frame 就好了,就像我们创建 view 一样,好理解。
- (void)drawRect:(CGRect)rect {
UIColor *color = [UIColor redColor];
[color set]; //设置线条颜色
UIBezierPath* path = [UIBezierPath bezierPathWithRect:CGRectMake(20, 20, 100, 80)];
path.lineWidth = 5.0;
path.lineCapStyle = kCGLineCapRound; //线条拐角
path.lineJoinStyle = kCGLineJoinRound; //终点处理
[path stroke];
}
6. 创建圆形或者椭圆形

- 使用
+ (UIBezierPath *)bezierPathWithOvalInRect:(CGRect)rect
这个方法创建圆形或者椭圆形。
- 传入的rect矩形参数绘制一个内切曲线,如果我们传入的rect是矩形就得到矩形的内切椭圆,如果传入的是 正方形得到的就是正方形的内切圆。
- (void)drawRect:(CGRect)rect {
UIColor *color = [UIColor redColor];
[color set];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(30, 30, 100, 100)];
path.lineWidth = 5.0;
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
[path stroke];
}
7. 创建一段弧线
使用+ (UIBezierPath *)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwis
这个方法创建一段弧线,介绍一下这个方法中的参数:
/*
ArcCenter: 原点
radius: 半径
startAngle: 开始角度
endAngle: 结束角度
clockwise: 是否顺时针方向
*/
- (void)drawRect:(CGRect)rect {
UIColor *color = [UIColor redColor];
[color set];
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:90 startAngle:0 endAngle:TO_RADIAUS(120) clockwise:YES];
path.lineWidth = 5.0;
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
[path stroke];
}
弧线的参考系:

8. 绘制二次贝塞尔曲线

- 使用
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint
这个方法绘制二次贝塞尔曲线。
- 曲线段在当前点开始,在指定的点结束,
- 一个控制点的切线定义。下图显示了两种曲线类型的相似,以及控制点和curve形状的关系:
- (void)drawRect:(CGRect)rect {
UIColor *color = [UIColor redColor];
[color set];
UIBezierPath *path = [UIBezierPath bezierPath];
path.lineWidth = 5.0;
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
/*
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint
Parameters
endPoint
The end point of the curve.
controlPoint
The control point of the curve.
*/
[path moveToPoint:CGPointMake(40, 150)];
[path addQuadCurveToPoint:CGPointMake(140, 200) controlPoint:CGPointMake(20, 40)];
[path stroke];
}
9. 绘制三次贝塞尔曲线

使用这个方法绘制三次贝塞尔曲线
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
Parameters
这个方法绘制三次贝塞尔曲线。曲线段在当前点开始,在指定的点结束,两个控制点的切线定义。下图显示了两种曲线类型的相似,以及控制点和curve形状的关系:

- (void)drawRect:(CGRect)rect {
/*
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
Parameters
endPoint
The end point of the curve.
controlPoint1
The first control point to use when computing the curve.
controlPoint2
The second control point to use when computing the curve.
*/
UIColor *color = [UIColor redColor];
[color set];
UIBezierPath *path = [UIBezierPath bezierPath];
path.lineWidth = 5.0;
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
[path moveToPoint:CGPointMake(20, 200)];
[path addCurveToPoint:CGPointMake(260, 200) controlPoint1:CGPointMake(140, 0) controlPoint2:CGPointMake(140, 400)];
[path stroke];
}
10. 画带圆角的矩形

使用+ (instancetype)bezierPathWithRect:(CGRect)rect;
这个方法绘制,这个方法和bezierPathWithRect:
类似,绘制一个带内切圆的矩形。
- (void)drawRect:(CGRect)rect {
UIColor *color = [UIColor redColor];
[color set]; //设置线条颜色
UIBezierPath* path = [UIBezierPath bezierPathWithRect:CGRectMake(20, 20, 100, 80)];
path.lineWidth = 5.0;
path.lineCapStyle = kCGLineCapRound; //线条拐角
path.lineJoinStyle = kCGLineJoinRound; //终点处理
[path stroke];
}
11. 指定矩形的某个角为圆角

使用+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
这个方法绘制。参数的意思:rect 绘制矩形的 frame,corners指定使哪个角为圆角,圆角类型为:
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 << 0,
UIRectCornerTopRight = 1 << 1,
UIRectCornerBottomLeft = 1 << 2,
UIRectCornerBottomRight = 1 << 3,
UIRectCornerAllCorners = ~0UL
};
用来指定需要设置的角。cornerRadii 圆角的半径
- (void)drawRect:(CGRect)rect {
UIColor *color = [UIColor redColor];
[color set];
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 100, 100) byRoundingCorners:UIRectCornerTopRight cornerRadii:CGSizeMake(20, 20)];
path.lineCapStyle = kCGLineCapRound;
path.lineJoinStyle = kCGLineJoinRound;
path.lineWidth = 5.0;
[path stroke];
}
四、图像处理
1. 图片拼接
// 注意, liangzhang图片的Rect若是发生重叠,则会导致图片覆盖
- (UIImage *)combine:(UIImage*)leftImage :(UIImage*)rightImage {
CGFloat width = leftImage.size.width * 2;
CGFloat height = leftImage.size.height;
CGSize offScreenSize = CGSizeMake(width, height);
UIGraphicsBeginImageContext(offScreenSize);
CGRect rect = CGRectMake(0, 0, width/2, height);
[leftImage drawInRect:rect];
rect.origin.x += width/2;
[rightImage drawInRect:rect];
UIImage* imagez = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return imagez;
}
2. 图片截取
/// 截图 保证清晰度
+ (UIImage *)editImageWithOldImage:(UIImage *)image OldFrame:(CGRect)oldFrame CropFrame:(CGRect)cropFrame Scale:(float)scale {
image = [self fixOrientation:image];
CGSize editSize = oldFrame.size;
CGSize imgSize = image.size;
CGFloat widthScale;
CGRect rct;
if (imgSize.width < editSize.width) {
widthScale = imgSize.width / editSize.width;
rct = CGRectMake(cropFrame.origin.x*widthScale, cropFrame.origin.y*widthScale, cropFrame.size.width*widthScale, cropFrame.size.height*widthScale);
} else {
widthScale = editSize.width / imgSize.width;
rct = CGRectMake(cropFrame.origin.x/widthScale, cropFrame.origin.y/widthScale, cropFrame.size.width/widthScale, cropFrame.size.height/widthScale);
}
CGPoint origin = CGPointMake(-rct.origin.x, -rct.origin.y);
UIImage *img = nil;
UIGraphicsBeginImageContextWithOptions(rct.size, NO, image.scale);
[image drawAtPoint:origin];
img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
/// 图片方向调整
+ (UIImage *)fixOrientation:(UIImage *)aImage {
// No-op if the orientation is already correct
if (aImage.imageOrientation == UIImageOrientationUp)
return aImage;
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;
switch (aImage.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}
switch (aImage.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
CGImageGetBitsPerComponent(aImage.CGImage), 0,
CGImageGetColorSpace(aImage.CGImage),
CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
switch (aImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
break;
default:
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
break;
}
// And now we just create a new UIImage from the drawing context
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
示意图:
拍照后只取框内部分

确认时只取框内部分

处理后就这样

3. 加水印
@interface UIImage(WaterMask)
- (UIImage*)addWaterMask:(NSString*)txt;
@end
@implementation UIImage(WaterMask)
- (UIImage*)addWaterMask:(NSString*)txt{
//1.创建位图上下文(size:开启多大的上下文,就会生成多大的图片)
UIGraphicsBeginImageContext(self.size);
//2.把图片绘制到上下文当中
[self drawAtPoint:CGPointZero];
//3.绘制水印(虽说UILabel可以快速实现这种效果,但是我们也可以绘制出来)
NSString *str = txt;
// 水印的 字体、颜色等样式 根据项目 按需调整
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSFontAttributeName] = [UIFont systemFontOfSize:20];
dict[NSForegroundColorAttributeName] = [UIColor redColor];
[str drawAtPoint:CGPointZero withAttributes:dict];
//4.从上下文当中生成一张图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//5.关闭位图上下文
UIGraphicsEndImageContext();
return newImage;
}
@end
4. 截屏处理
@interface UIView(ScreenShot)
- (UIImage*)screenshot;
@end
@implementation UIView(ScreenShot)
- (UIImage*)screenshot{
//生成图片
//1.开启一个位图上下文
UIGraphicsBeginImageContext(self.bounds.size);
//2.把View的内容绘制到上下文当中
CGContextRef ctx = UIGraphicsGetCurrentContext();
//UIView内容想要绘制到上下文当中, 必须使用渲染的方式
[self.layer renderInContext:ctx];
//3.从上下文当中生成一张图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//4.关闭上下文
UIGraphicsEndImageContext();
//把图片转成二进制流
// //NSData *data = UIImageJPEGRepresentation(newImage, 1);
// NSData *data = UIImagePNGRepresentation(newImage);
//
// [data writeToFile:@"/Users/lifengfeng/Downloads/imlifengfeng.jpg" atomically:YES];
return newImage;
}
@end
5. 图片解码
关于 Core Graphics
图片解码 相关原理和过程,我推荐看这几篇文章:
五、其它功能
1. 绘图功能|绘制形状(直线、曲线、矩形、椭圆等)
Objective-C示例:
// Objective-C示例:
#import <UIKit/UIKit.h>
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 绘制直线
CGContextMoveToPoint(context, 50, 50);
CGContextAddLineToPoint(context, 150, 150);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextStrokePath(context);
// 绘制曲线
CGContextMoveToPoint(context, 50, 100);
CGContextAddQuadCurveToPoint(context, 100, 50, 150, 100);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextStrokePath(context);
// 绘制矩形
CGContextAddRect(context, CGRectMake(50, 150, 100, 50));
CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
CGContextFillPath(context);
// 绘制椭圆
CGContextAddEllipseInRect(context, CGRectMake(50, 200, 100, 50));
CGContextSetStrokeColorWithColor(context, [UIColor purpleColor].CGColor);
CGContextStrokePath(context);
}
Swift示例:
// Swift示例:
override func draw(_ rect: CGRect) {
if let context = UIGraphicsGetCurrentContext() {
// 绘制直线
context.move(to: CGPoint(x: 50, y: 50))
context.addLine(to: CGPoint(x: 150, y: 150))
context.setStrokeColor(UIColor.red.cgColor)
context.strokePath()
// 绘制曲线
context.move(to: CGPoint(x: 50, y: 100))
context.addQuadCurve(to: CGPoint(x: 150, y: 100), control: CGPoint(x: 100, y: 50))
context.setStrokeColor(UIColor.blue.cgColor)
context.strokePath()
// 绘制矩形
context.addRect(CGRect(x: 50, y: 150, width: 100, height: 50))
context.setFillColor(UIColor.green.cgColor)
context.fillPath()
// 绘制椭圆
context.addEllipse(in: CGRect(x: 50, y: 200, width: 100, height: 50))
context.setStrokeColor(UIColor.purple.cgColor)
context.strokePath()
}
}
2 绘图功能|绘制路径(自定义路径、曲线路径等)
Objective-C示例:
// Objective-C示例:
#import <UIKit/UIKit.h>
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 绘制自定义路径
CGContextMoveToPoint(context, 50, 50);
CGContextAddLineToPoint(context, 150, 50);
CGContextAddLineToPoint(context, 100, 100);
CGContextClosePath(context);
// 绘制曲线路径
CGContextMoveToPoint(context, 50, 150);
CGContextAddQuadCurveToPoint(context, 100, 200, 150, 150);
// 设置路径样式
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
// 绘制路径
CGContextDrawPath(context, kCGPathFillStroke);
}
Swift示例:
// Swift示例:
override func draw(_ rect: CGRect) {
if let context = UIGraphicsGetCurrentContext() {
// 绘制自定义路径
context.move(to: CGPoint(x: 50, y: 50))
context.addLine(to: CGPoint(x: 150, y: 50))
context.addLine(to: CGPoint(x: 100, y: 100))
context.closePath()
// 绘制曲线路径
context.move(to: CGPoint(x: 50, y: 150))
context.addQuadCurve(to: CGPoint(x: 150, y: 150), control: CGPoint(x: 100, y: 200))
// 设置路径样式
context.setLineWidth(2.0)
context.setStrokeColor(UIColor.blue.cgColor)
// 绘制路径
context.drawPath(using: .fillStroke)
}
}
3 绘图功能|绘制图像(位图图像、矢量图像等)
Objective-C示例:
// Objective-C示例:
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 创建绘图上下文
UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
// 绘制矩形
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextFillRect(context, CGRectMake(50, 50, 100, 100));
// 绘制图像
UIImage *image = [UIImage imageNamed:@"exampleImage"];
[image drawInRect:CGRectMake(20, 20, 50, 50)];
// 获取绘制的图像
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// 显示绘制的图像
UIImageView *imageView = [[UIImageView alloc] initWithImage:resultImage];
[self.view addSubview:imageView];
}
@end
Swift示例:
// Swift示例:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 创建绘图上下文
UIGraphicsBeginImageContextWithOptions(CGSize(width: 200, height: 200), false, 0.0)
if let context = UIGraphicsGetCurrentContext() {
// 绘制矩形
context.setFillColor(UIColor.blue.cgColor)
context.fill(CGRect(x: 50, y: 50, width: 100, height: 100))
// 绘制图像
if let image = UIImage(named: "exampleImage") {
image.draw(in: CGRect(x: 20, y: 20, width: 50, height: 50))
}
// 获取绘制的图像
if let resultImage = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
// 显示绘制的图像
let imageView = UIImageView(image: resultImage)
imageView.frame = CGRect(x: 100, y: 100, width: 200, height: 200)
view.addSubview(imageView)
}
}
}
}
4 图像处理|加载
、绘制
、裁剪
、变换
、合成
、渲染
Objective-C示例:
// Objective-C示例:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIImage *originalImage = [UIImage imageNamed:@"original_image"];
// 创建绘图上下文
UIGraphicsBeginImageContextWithOptions(originalImage.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
// 绘制原始图像
[originalImage drawInRect:CGRectMake(0, 0, originalImage.size.width, originalImage.size.height)];
// 裁剪图像
CGContextClipToRect(context, CGRectMake(0, 0, originalImage.size.width / 2, originalImage.size.height));
// 创建变换矩阵
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);
CGContextConcatCTM(context, transform);
// 绘制变换后的图像
UIImage *transformedImage = UIGraphicsGetImageFromCurrentImageContext();
// 合成图像
UIImage *compositeImage = [UIImage imageNamed:@"overlay_image"];
[compositeImage drawInRect:CGRectMake(0, 0, originalImage.size.width, originalImage.size.height) blendMode:kCGBlendModeNormal alpha:0.5];
// 渲染图像
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, originalImage.size.width, originalImage.size.height)];
self.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
[self.view addSubview:self.imageView];
UIGraphicsEndImageContext();
}
@end
Swift示例:
// Swift示例:
import UIKit
class ViewController: UIViewController {
var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
if let originalImage = UIImage(named: "original_image") {
// 创建绘图上下文
UIGraphicsBeginImageContextWithOptions(originalImage.size, false, 0.0)
if let context = UIGraphicsGetCurrentContext() {
// 绘制原始图像
originalImage.draw(in: CGRect(x: 0, y: 0, width: originalImage.size.width, height: originalImage.size.height))
// 裁剪图像
context.clip(to: CGRect(x: 0, y: 0, width: originalImage.size.width / 2, height: originalImage.size.height))
// 创建变换矩阵
let transform = CGAffineTransform(rotationAngle: CGFloat.pi / 4)
context.concatenate(transform)
// 绘制变换后的图像
let transformedImage = UIGraphicsGetImageFromCurrentImageContext()
// 合成图像
if let compositeImage = UIImage(named: "overlay_image") {
compositeImage.draw(in: CGRect(x: 0, y: 0, width: originalImage.size.width, height: originalImage.size.height), blendMode: .normal, alpha: 0.5)
}
// 渲染图像
imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: originalImage.size.width, height: originalImage.size.height))
imageView.image = UIGraphicsGetImageFromCurrentImageContext()
view.addSubview(imageView)
UIGraphicsEndImageContext()
}
}
}
}
5 文本渲染|渲染文本内容,控制文本样式、排版布局
Objective-C示例:
// Objective-C示例:
#import <UIKit/UIKit.h>
- (void)drawTextWithCustomStyle {
CGContextRef context = UIGraphicsGetCurrentContext();
NSString *text = @"Hello, Core Graphics!";
UIFont *font = [UIFont systemFontOfSize:16];
UIColor *textColor = [UIColor redColor];
NSDictionary *attributes = @{NSFontAttributeName: font, NSForegroundColorAttributeName: textColor};
CGSize textSize = [text sizeWithAttributes:attributes];
CGRect textRect = CGRectMake(50, 50, textSize.width, textSize.height);
[text drawInRect:textRect withAttributes:attributes];
}
Swift示例:
// Swift示例:
import UIKit
func drawTextWithCustomStyle() {
if let context = UIGraphicsGetCurrentContext() {
let text = "Hello, Core Graphics!"
let font = UIFont.systemFont(ofSize: 16)
let textColor = UIColor.red
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
let attributes: [NSAttributedString.Key: Any] = [
.font: font,
.foregroundColor: textColor,
.paragraphStyle: paragraphStyle
]
let textSize = text.size(withAttributes: attributes)
let textRect = CGRect(x: 50, y: 50, width: textSize.width, height: textSize.height)
text.draw(in: textRect, withAttributes: attributes)
}
}
6 图形上下文|绘图 颜色、线宽、填充模式
Objective-C示例:
// Objective-C示例:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置绘图颜色
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
// 设置线宽
CGContextSetLineWidth(context, 2.0);
// 绘制矩形
CGContextAddRect(context, CGRectMake(50, 50, 100, 100));
// 填充矩形
CGContextFillPath(context);
}
Swift示例:
// Swift示例:
override func draw(_ rect: CGRect) {
if let context = UIGraphicsGetCurrentContext() {
// 设置绘图颜色
context.setFillColor(UIColor.blue.cgColor)
context.setStrokeColor(UIColor.red.cgColor)
// 设置线宽
context.setLineWidth(2.0)
// 绘制矩形
context.addRect(CGRect(x: 50, y: 50, width: 100, height: 100))
// 填充矩形
context.fillPath()
}
}
7 颜色和渲染|填充颜色
、描边颜色
、渐变色
Objective-C示例:
// Objective-C示例:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 填充颜色
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextFillRect(context, rect);
// 描边颜色
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextStrokeRect(context, CGRectMake(50, 50, 100, 100));
// 创建渐变色
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = {0.0, 1.0};
NSArray *colors = @[(id)[UIColor redColor].CGColor, (id)[UIColor blueColor].CGColor];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
// 绘制渐变色
CGContextDrawLinearGradient(context, gradient, CGPointMake(0, 0), CGPointMake(0, CGRectGetHeight(rect)), kCGGradientDrawsBeforeStartLocation);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
Swift示例:
// Swift示例:
override func draw(_ rect: CGRect) {
if let context = UIGraphicsGetCurrentContext() {
// 填充颜色
context.setFillColor(UIColor.blue.cgColor)
context.fill(rect)
// 描边颜色
context.setStrokeColor(UIColor.red.cgColor)
context.stroke(CGRect(x: 50, y: 50, width: 100, height: 100))
// 创建渐变色
let colorSpace = CGColorSpaceCreateDeviceRGB()
let locations: [CGFloat] = [0.0, 1.0]
if let gradient = CGGradient(colorsSpace: colorSpace, colors: [UIColor.red.cgColor, UIColor.blue.cgColor] as CFArray, locations: locations) {
// 绘制渐变色
context.drawLinearGradient(gradient, start: CGPoint(x: 0, y: 0), end: CGPoint(x: 0, y: rect.height), options: [])
}
}
}
8 图形变换|平移、旋转、缩放
Objective-C示例:
// Objective-C示例:
#import <UIKit/UIKit.h>
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 平移
CGContextTranslateCTM(context, 50, 50);
// 旋转
CGContextRotateCTM(context, M_PI_4); // 45度
// 缩放
CGContextScaleCTM(context, 1.5, 1.5);
// 绘制一个矩形
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextFillRect(context, CGRectMake(0, 0, 100, 100));
}
Swift示例:
// Swift示例:
override func draw(_ rect: CGRect) {
if let context = UIGraphicsGetCurrentContext() {
// 平移
context.translateBy(x: 50, y: 50)
// 旋转
context.rotate(by: CGFloat.pi / 4) // 45度
// 缩放
context.scaleBy(x: 1.5, y: 1.5)
// 绘制一个矩形
context.setFillColor(UIColor.blue.cgColor)
context.fill(CGRect(x: 0, y: 0, width: 100, height: 100))
}
}
9 性能优化
Objective-C示例:
// Objective-C示例:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置绘制属性,如颜色、线宽等
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineWidth(context, 2.0);
// 性能优化:禁止图形上下文的自动平滑处理
CGContextSetAllowsAntialiasing(context, NO);
// 性能优化:设置图形上下文的属性
CGContextSetShouldAntialias(context, YES); // 开启抗锯齿
CGContextSetAllowsAntialiasing(context, YES); // 允许抗锯齿
CGContextSetInterpolationQuality(context, kCGInterpolationHigh); // 设置插值质量为高
// 绘制大量图形
for (int i = 0; i < 1000; i++) {
CGContextMoveToPoint(context, i, 0);
CGContextAddLineToPoint(context, i, 100);
}
// 执行绘制
CGContextStrokePath(context);
}
Swift示例:
// Swift示例:
override func draw(_ rect: CGRect) {
if let context = UIGraphicsGetCurrentContext() {
// 设置绘制属性,如颜色、线宽等
context.setStrokeColor(UIColor.red.cgColor)
context.setLineWidth(2.0)
// 性能优化:禁止图形上下文的自动平滑处理
context.setAllowsAntialiasing(false)
// 性能优化:设置图形上下文的属性
context.setShouldAntialias(true) // 开启抗锯齿
context.setAllowsAntialiasing(true) // 允许抗锯齿
context.setInterpolationQuality(.high) // 设置插值质量为高
// 绘制大量图形
for i in 0..<1000 {
context.move(to: CGPoint(x: i, y: 0))
context.addLine(to: CGPoint(x: i, y: 100))
}
// 执行绘制
context.strokePath()
}
}
六、经典第三方库
关于文本的处理,在这里推荐一个第三方库:YYText
(有此处提及的Core Graphics文本处理引出,但与Core Graphics框架无关)
关于K线图、柱状图、饼状图的绘制,第三方库推荐(我们在实际开发中,也许只是借鉴人家三方库的写法,去简化封装我们自己需要的图)