[TOC]
Core Graphics 2D绘图框架
Core Graphics 2D绘图框架
2D: 是一个平面 x轴 y轴
3D: 是一个立体 x轴 y轴 z轴
API介绍
#include <CoreGraphics/CGBase.h>
#include <CoreGraphics/CGAffineTransform.h> //放射矩阵
#include <CoreGraphics/CGColor.h>
#include <CoreGraphics/CGColorSpace.h> //颜色空间
#include <CoreGraphics/CGFont.h>
#include <CoreGraphics/CGGradient.h> //渐变颜色
#include <CoreGraphics/CGImage.h>
#include <CoreGraphics/CGPath.h> //路径
#include <CoreGraphics/CGPattern.h>
#include <CoreGraphics/CGPDFDocument.h>
#include <CoreGraphics/CGShading.h>
///全部知识点: 详细参见API文档
Core Graphics三个核心概念:
1.上下文: CGContextRef
Core Graphics 使用图形上下文进行工作,这个上下文的作用等于"画布"。
上下文可以是以下类型:
1)UIView:
自定义一个UIView的子类,在UIView中调用drawRect:方法时,会自动准备好一个图形上下文:
//获取上下文:
CGContextRef ctx = UIGraphicsGetCurrentContext();
drawRect:是系统的方法,不要从代码里面直接调用 drawRect:,而应该使用setNeedsDisplay
重绘.
2)UIImage:
//先创建内存中的图片
UIGraphicsBeginImageContext(CGSizeMake(320,480))
//获取内存中图片的CGContextRef
CGContextRef ctx = UIGraphicsGetCurrentContext()
//撤销上下文
UIGraphicsEndImageContext();
3)PDF文件:
向PDF文件绘图 CGPDFContext: A type of CGContextRef for drawing PDF content.
2.路径: path
几何路径:
==点(Point) 线(Line) 曲线(贝塞尔曲线,Curve) 弧(Arc) 矩形(Rect) 椭圆/圆(Ellipse) 放射矩阵转换(transform)==
1) CGContext可以添加路径
- 路径 path :moveToPoint addLineToPoint addArcWithCenter addCurveToPoint
- 剪裁路径 Clip Path
- 仿射变形矩阵
CGAffineTransform
2) 使用CGPath 创建路径(比较常用,CGPathRef
)
3) 使用UIBezierPath
创建路径 (UIBezierPath是CGPathRef的封装)
3.绘制方式
线型
- 线条宽度:Line Width
- 点线(LineDash): 也就是虚线模式,增加线宽可以实现条形码
- 端点(LineCap)
- 连接点( lineJoin)
颜色:
- 画笔颜色 stroke color
- 填充颜色 Fill Color
阴影:shadow
叠加模式:BlendMode
默认情况下,后面的绘图会完全覆盖前面的绘图。
- 绘制方式
- 轮廓绘制 Stroke
- 填充绘制 Fill
4.特别绘制
- 文字绘制: Text
- 绘制图片:
- 在内存中绘图: UIGraphicsBeginImageContext/UIGraphicsEndImageContext
- 图片重绘: CGContextDrawImage
- 颜色渐变:
CGContextDrawLinearGradient;CGContextDrawRadialGradient
- 瓷砖块模式:typedef struct CGPattern *CGPatternRef;
- 其他:Shading、雕文、PDF
应用
一、基本图形的绘制
1.直线 2.矩形 3.椭圆 4.虚线
//视图显示的时候默认会调用这个方法
//我们一般不会直接调用这个方法
//如果需要调用这个方法里面的代码,可以调用视图对象的 setNeedsDisplay 方法
-(void)drawRect:(CGRect)rect
{
//获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//1.画直线
//路径
CGContextMoveToPoint(ctx, 20, 20);
CGContextAddLineToPoint(ctx, 100, 20);
//绘制方式
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor); //设置颜色
CGContextSetLineWidth(ctx, 10); //设置宽度
CGContextStrokePath(ctx); //绘制
//两条直线交叉
//路径
CGContextMoveToPoint(ctx, 30, 30);
CGContextAddLineToPoint(ctx, 30, 100);
CGContextAddLineToPoint(ctx, 100, 70);
//绘制方式
CGContextSetLineWidth(ctx, 4);
CGContextSetLineCap(ctx, kCGLineCapRound); //设置交叉处:圆形:kCGLineCapRound 方形:kCGLineCapSquare
CGContextStrokePath(ctx); //绘制
//另一种方法:c语言数组模式
const CGPoint points3[] = {CGPointMake(120, 30),CGPointMake(120, 100),CGPointMake(120, 100),CGPointMake(200, 70)};
CGContextSetLineCap(ctx, kCGLineCapSquare); //方形交叉处
CGContextStrokeLineSegments(ctx, points3, 4); //连线 也可以画多边形 多线条
//2.矩形
//方法一:
//路径
CGContextMoveToPoint(ctx, 30, 120);
CGContextAddRect(ctx, CGRectMake(30, 120, 130, 20));
//绘制方式
CGContextSetStrokeColorWithColor(ctx, [UIColor magentaColor].CGColor); //设置颜色
CGContextSetLineWidth(ctx, 3); //设置宽度
CGContextStrokePath(ctx); //绘制
//方法二:系统方法
CGContextStrokeRect(ctx, CGRectMake(30, 145, 130, 10));
//实心
//设置颜色
CGContextSetFillColorWithColor(ctx, [UIColor purpleColor].CGColor);
CGContextFillRect(ctx, CGRectMake(170, 120, 100, 30));
//3.椭圆
//方法一:
//路径
CGContextMoveToPoint(ctx, 30, 120);
CGContextAddEllipseInRect(ctx, CGRectMake(30, 200, 100, 40));
//绘制方式
CGContextSetStrokeColorWithColor(ctx, [UIColor purpleColor].CGColor); //设置颜色
CGContextSetLineWidth(ctx, 3); //设置宽度
CGContextStrokePath(ctx); //绘制
//方法二:系统方法
//实心
CGContextFillEllipseInRect(ctx, CGRectMake(150, 200, 100, 40));
//4.虚线
/*
虚线画笔:
CGContextSetLineDash(<#CGContextRef c#>, <#CGFloat phase#>, <#const CGFloat *lengths#>, <#size_t count#>)
第一个参数:绘图的上下文
第二个参数:相位:起始点是空格 还是线条
第三个参数:数组: 宽度 间隔宽度
第四个参数:数组的长度
*/
//1)设置线型为虚线模式
const CGFloat dash1[] = {10,10};
CGContextSetLineDash(ctx, 0, dash1, 2);
//画线
const CGPoint points5[] = {CGPointMake(30, 260),CGPointMake(300, 260)};
CGContextStrokeLineSegments(ctx, points5, 2);
//另外一条虚线
const CGFloat dash2[] = {10,20};
CGContextSetLineDash(ctx, 0, dash2, 2);
const CGPoint points6[] = {CGPointMake(30, 300),CGPointMake(300, 300)};
CGContextStrokeLineSegments(ctx, points6, 2);
//另一条虚线
const CGFloat dash3[] = {10,20,10};
CGContextSetLineDash(ctx, 0, dash3, 3);
const CGPoint points7[] = {CGPointMake(30, 340),CGPointMake(300, 340)};
CGContextStrokeLineSegments(ctx, points7, 2);
//设置相位
const CGFloat dash4[] = {10,10};
CGContextSetLineDash(ctx, 5, dash4, 2);
const CGPoint points8[] = {CGPointMake(30, 380),CGPointMake(300, 380)};
CGContextStrokeLineSegments(ctx, points8, 2);
//虚线矩形
CGContextStrokeRect(ctx, CGRectMake(30, 400, 100, 60));
//虚线椭圆
CGContextStrokeEllipseInRect(ctx, CGRectMake(150, 400, 100, 60));
}
二、文字绘制
//绘制文字
-(void)drawRect:(CGRect)rect
{
//获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//方法一:
NSString *str1 = @"绘制文字?";
[str1 drawAtPoint:CGPointMake(30, 50) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:20],NSForegroundColorAttributeName:[UIColor redColor]}];
CGContextSetTextDrawingMode(ctx, kCGTextStroke); //绘制模式:空心文字
NSString *str2 = @"空心文字 kCGTextStroke";
[str2 drawAtPoint:CGPointMake(10, 100) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:28],NSForegroundColorAttributeName:[UIColor greenColor]}];
//设置阴影
/*
第一个参数:绘图的上下文
第二个参数:阴影的偏移量(CGSize类型的值,第一个值是x方向的偏移量,正数表示阴影在文字的右边,第二个值是y方向上的偏移量,正数表示阴影在文字的下边)
第三个参数:阴影的透明度(0~1)
*/
CGContextSetShadow(ctx, CGSizeMake(10, 10), 1);
NSString *str3 = @"阴影文字?";
[str3 drawAtPoint:CGPointMake(30, 150) withAttributes:@{NSForegroundColorAttributeName:[UIColor brownColor],NSFontAttributeName:[UIFont systemFontOfSize:20]}];
//
CGContextSetTextDrawingMode(ctx, kCGTextFillStroke);
NSString *str4 = @"kCGTextFillStroke";
[str4 drawAtPoint:CGPointMake(30, 200) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor purpleColor]}];
//***方式二:
CGContextSetFont(ctx, CGFontCreateWithFontName(CFSTR("PingFangSC-Regula")));
CGContextSetFontSize(ctx, 30);
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor); //颜色设置(fill)
CGContextSetStrokeColorWithColor(ctx, [UIColor greenColor].CGColor); //颜色设置(Stroke)
CGContextSetTextMatrix(ctx, CGAffineTransformMake(1, 0, 0, -1, 0, 0)); //字体倒置
CGContextSetTextPosition(ctx, 100, 100); //起点位置
CGContextSetTextDrawingMode(ctx, kCGTextFillStroke);
NSString *str5 = @"测试方式二";
[str5 drawAtPoint:CGPointMake(30, 250) withAttributes:nil]; //绘制
}
三、绘制图片
- 在内存中绘图: UIGraphicsBeginImageContext/UIGraphicsEndImageContext
- 图片重绘: CGContextDrawImage
- (UIImage*) drawImage
{
//1.调用UIGraphicsBeginImageContext(CGSize size)准备绘图环境
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
//2.调用UIGraphicsGetCurrentContext获取绘图上下文CGContextRef
CGContextRef ctx = UIGraphicsGetCurrentContext();
//3.在CGContextRef中绘图
// ---------下面开始向内存中绘制图形---------
//方法一:直接绘制
// 设置线宽
CGContextSetLineWidth(ctx, 8);
// 设置线条颜色
CGContextSetRGBStrokeColor(ctx, 0 , 1, 0 , 1);
// 绘制一个矩形边框
CGContextStrokeRect(ctx , CGRectMake(30 , 30 , 120 , 60));
// 设置填充颜色
CGContextSetRGBFillColor(ctx, 1, 1, 0 , 1);
// 绘制一个矩形边框
CGContextFillRect(ctx , CGRectMake(180 , 30 , 120 , 60));
// 设置线条颜色
CGContextSetRGBStrokeColor(ctx, 0, 1 , 1 , 1);
// 绘制一个椭圆
CGContextStrokeEllipseInRect(ctx , CGRectMake(30 , 120 , 120 , 60));
// 设置填充颜色
CGContextSetRGBFillColor(ctx, 1, 0 , 1 , 1);
// 填充一个椭圆
CGContextFillEllipseInRect(ctx , CGRectMake(180 , 120 , 120 , 60));
//方法二:将图片绘制到Context中
//CGContextDrawImage(CGContextRef cg_nullable c, CGRect rect,CGImageRef cg_nullable image);
//CGContextDrawTiledImage(CGContextRef cg_nullable c, CGRect rect,CGImageRef cg_nullable image);
//CGContextDrawImage(ctx, CGRectMake(30 , 30 , 120 , 60), image.cgImage);
// ---------结束绘图---------
//4.调用UIGraphicsGetImageFromCurrentImageContext获取Context中的图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//5.调用UIGraphicsEndImageContext(void)关闭绘图环境
UIGraphicsEndImageContext();
return newImage;
}
//在内存中绘制图片
- (UIImage *)drawImage {
UIGraphicsBeginImageContext(CGSizeMake(50, 50)); //开始:新建一个基于UIImage的上下文,上下文位于栈顶
//获取当前上下文(即刚刚新建的上下文)
CGContextRef ctx = UIGraphicsGetCurrentContext();
//路径
CGContextBeginPath(ctx);
CGContextAddArc(ctx, 25, 25, 10, 0, 2*M_PI, YES);
//绘图
CGContextSetRGBFillColor(ctx, 0.8, 0.5, 0.3, 1);
CGContextFillPath(ctx);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); //从上下文中获取图片
UIGraphicsEndImageContext(); //撤销上下文
return newImage;
}
//截屏操作
-(UIImage *)imageWithScreenContentsInView:(UIView *)view
{
//根据屏幕大小,获取上下文
UIGraphicsBeginImageContext([[UIScreen mainScreen] bounds].size);
[view.layer renderInContext:UIGraphicsGetCurrentContext()]; //渲染到上下文
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
//缩小图片
-(UIImage*)resizeImage:(UIImage*)image toSize:(CGSize)size
{
UIGraphicsBeginImageContext(size);
CGContextRef ctx= UIGraphicsGetCurrentContext();
//CGContextTranslateCTM(ctx, 0.0, size.height);
//CGContextScaleCTM(ctx, 1.0, -1.0);
//重绘image
CGContextDrawImage(ctx,CGRectMake(0.0f, 0.0f, size.width, size.height), image.CGImage);
//根据指定的size大小得到新的image
UIImage* scaled= UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaled;
}
四、复杂图形的绘制
绘图要充分理解三角函数和坐标系。参见数学笔记!
复杂的图形,都是基于简单路径加上复杂函数。
#ifndef PathTest_FKContext_h
#define PathTest_FKContext_h
/*
该方法负责绘制圆角矩形
x1、y2:是圆角矩形左上角的座标。
width、height:控制圆角举行的宽、高
radius:控制圆角矩形的四个圆角的半径
*/
void CGContextAddRoundRect(CGContextRef c, CGFloat x1 , CGFloat y1
, CGFloat width , CGFloat height , CGFloat radius)
{
// 移动到左上角
CGContextMoveToPoint (c, x1 + radius , y1);
// 添加一条连接到右上角的线段
CGContextAddLineToPoint(c , x1 + width - radius, y1);
// 添加一段圆弧
CGContextAddArcToPoint(c , x1 + width , y1, x1 + width
, y1 + radius, radius);
// 添加一条连接到右下角的线段
CGContextAddLineToPoint(c , x1 + width, y1 + height - radius);
// 添加一段圆弧
CGContextAddArcToPoint(c , x1 + width, y1 + height
, x1 + width - radius , y1 + height , radius);
// 添加一条连接到左下角的线段
CGContextAddLineToPoint(c , x1 + radius, y1 + height);
// 添加一段圆弧
CGContextAddArcToPoint(c , x1, y1 + height , x1
, y1 + height - radius , radius);
// 添加一条连接到左上角的线段
CGContextAddLineToPoint(c , x1 , y1 + radius);
// 添加一段圆弧
CGContextAddArcToPoint(c , x1 , y1 , x1 + radius , y1 , radius);
}
/*
该方法负责绘制多角星。
n:该参数通常应设为奇数,控制绘制N角星。
dx、dy:控制N角星的中心。
size:控制N角星的大小
*/
void CGContextAddStar(CGContextRef c , NSInteger n
, CGFloat dx , CGFloat dy , NSInteger size)
{
CGFloat dig = 4 * M_PI / n ;
// 移动到指定点
CGContextMoveToPoint(c , dx , dy + size);
for(int i = 1 ; i <= n ; i++)
{
CGFloat x = sin(i * dig);
CGFloat y = cos(i * dig);
// 绘制从当前点连接到指定点的线条
CGContextAddLineToPoint(c , x * size + dx ,y * size + dy);
}
}
/*
该方法负责绘制花朵。
n:该参数控制花朵的花瓣数。
dx、dy:控制花朵的位置。
size:控制花朵的大小
length:控制花瓣的长度
*/
void CGContextAddFlower(CGContextRef c , NSInteger n
, CGFloat dx , CGFloat dy , CGFloat size , CGFloat length)
{
// 移动到指定点
CGContextMoveToPoint(c , dx , dy + size);
CGFloat dig = 2 * M_PI / n;
// 采用循环添加n段二次曲线路径
for(int i = 1; i < n + 1 ; i++)
{
// 结算控制点坐标
CGFloat ctrlX = sin((i - 0.5) * dig) * length + dx;
CGFloat ctrlY= cos((i - 0.5 ) * dig) * length + dy;
// 结算结束点的坐标
CGFloat x = sin(i * dig) * size + dx;
CGFloat y =cos(i * dig) * size + dy;
// 添加二次曲线路径
CGContextAddQuadCurveToPoint(c, ctrlX , ctrlY , x , y);
}
}
- 折线图
- 柱状图
- 饼状图
- 五角星
- 花瓣
-(void)drawRect:(CGRect)rect
{
//获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//绘制模式
CGContextSetStrokeColorWithColor(ctx, [UIColor magentaColor].CGColor);
CGContextSetLineWidth(ctx, 3);
//1.绘制圆角矩形
[self drawMyRectWithCornerX:10 y:10 radius:10 width:200 height:80 ctx:ctx]; //路径
CGContextStrokePath(ctx);
//2.绘制正多边形
[self drawMyShapeWithCenterX:200 centerY:200 radius:50 num:5 ctx:ctx]; //路径
CGContextStrokePath(ctx);
//3. 柱形图
[self drawZhuXingTu:ctx];
}
/*
圆角矩形路径:
@param x:起始点的横坐标
@param y:起始点的纵坐标
@param radius:圆角弧形的半径
@param width:矩形的宽度
@param height:矩形的高度
@param ctx:绘图上下文
*/
- (void)drawMyRectWithCornerX:(CGFloat)x y:(CGFloat)y radius:(CGFloat)radius width:(CGFloat)width height:(CGFloat)height ctx:(CGContextRef)ctx
{
//路径:
CGContextMoveToPoint(ctx, x+radius, y); //起始点
CGContextAddLineToPoint(ctx, x+width-radius, y);
//圆弧 (x1,y1) 第一个控制点 (x2,y2) 第二个控制点
CGContextAddArcToPoint(ctx, x+width, y, x+width, y+radius, radius);
//直线
CGContextAddLineToPoint(ctx, x+width, y+height-radius);
//弧线
CGContextAddArcToPoint(ctx, x+width, y+height, x+width-radius, y+height, radius);
//直线
CGContextAddLineToPoint(ctx, x+radius, y+height);
//弧线
CGContextAddArcToPoint(ctx, x, y+height, x, y+height-radius, radius);
//直线
CGContextAddLineToPoint(ctx, x, y+radius);
//弧线
CGContextAddArcToPoint(ctx, x, y, x+radius, y, radius);
}
/*
多边形路径:
@param x:正多边形的中心点的横坐标
@param y:正多边形的中心点的纵坐标
@param raduis:正多边形外接圆的半径
@param num:是正几边形
@param ctx:绘图的上下文
*/
- (void)drawMyShapeWithCenterX:(CGFloat)x centerY:(CGFloat)y radius:(CGFloat)radius num:(NSInteger)num ctx:(CGContextRef)ctx
{
CGContextMoveToPoint(ctx, x+radius, y);
for (int i=1; i<=num; i++) {
//计算顶点的位置
CGPoint point = CGPointMake(x+radius*cos(2*M_PI*i/num), y+radius*sin(2*M_PI*i/num));
//连线
CGContextAddLineToPoint(ctx, point.x, point.y);
}
}
/**
柱形图
思路: 背景矩形 + 2条直线(xy轴)和 3个矩形
*/
- (void)drawZhuXingTu:(CGContextRef)ctx {
//路径:
//背景矩形
CGContextSetFillColorWithColor(ctx, [UIColor lightGrayColor].CGColor);
CGContextFillRect(ctx, CGRectMake(10, 300, 100, 100));
//x轴
CGContextMoveToPoint(ctx, 20, 390);
CGContextAddLineToPoint(ctx, 110, 390);
//y轴
CGContextMoveToPoint(ctx, 20, 390);
CGContextAddLineToPoint(ctx, 20, 310);
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextStrokePath(ctx); //绘制直线
//柱形矩形
CGContextSetFillColorWithColor(ctx, [UIColor magentaColor].CGColor);
CGContextFillRect(ctx, CGRectMake(30, 340, 15, 50));
CGContextSetFillColorWithColor(ctx, [UIColor greenColor].CGColor);
CGContextFillRect(ctx, CGRectMake(55, 330, 15, 60));
CGContextSetFillColorWithColor(ctx, [UIColor blueColor].CGColor);
CGContextFillRect(ctx, CGRectMake(85, 320, 15, 70));
}
颜色渐变:
CGContextDrawLinearGradient;
CGContextDrawRadialGradient
颜色渐变
Drawing With a Gradient
CGContextDrawLinearGradient //线性渐变
Paints a gradient fill that varies along the line defined by the provided starting and ending points.
CGContextDrawRadialGradient //圆形渐变
Paints a gradient fill that varies along the area defined by the provided starting and ending circles.
CGContextDrawLinearGradient(CGContextRef cg_nullable c,CGGradientRef cg_nullable gradient, CGPoint startPoint, CGPoint endPoint,CGGradientDrawingOptions options);
CGContextDrawRadialGradient(CGContextRef cg_nullable c,CGGradientRef cg_nullable gradient, CGPoint startCenter, CGFloat startRadius,CGPoint endCenter, CGFloat endRadius, CGGradientDrawingOptions options);
最后一个参数:
typedef CF_OPTIONS (uint32_t, CGGradientDrawingOptions) {
kCGGradientDrawsBeforeStartLocation = (1 << 0),
kCGGradientDrawsAfterEndLocation = (1 << 1)
};
参数2:
typedef struct CGGradient *CGGradientRef;
//初始化API:
CGGradientCreateWithColorComponents(CGColorSpaceRef cg_nullable space, const CGFloat * cg_nullable components,const CGFloat * __nullable locations, size_t count);
//参数1:颜色空间:typedef struct CGColorSpace *CGColorSpaceRef;
CGColorSpaceCreateDeviceGray(void);
CGColorSpaceCreateDeviceRGB(void);
CGColorSpaceCreateDeviceCMYK(void);
//components:颜色数组
//locations: 0~1.0 颜色的位置 NULL表示均匀分布
//count: 颜色的个数
//Demo:
CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat components[16] = {0.0, 0.0, 1.0, 0.5,
1.0, 1.0, 1.0, 0.8,
1.0, 1.0, 1.0, 0.8,
0.0, 0.0, 1.0, 0.5};
CGFloat locations[4] = {0.0, 0.33, 0.66, 1.0};
//Create the gradient.
CGGradientRef myGradient = CGGradientCreateWithColorComponents(myColorSpace, components, locations, 4);
CGPoint start, end;
start = CGPointMake(CGRectGetMidX(theRect), CGRectGetMinY(theRect));
end = CGPointMake(CGRectGetMidX(theRect), CGRectGetMaxY(theRect));
//Draw.
CGContextDrawLinearGradient(context, myGradient, start, end, 0);
瓷砖块模式
typedef struct CGPattern *CGPatternRef;
其他:Shading、雕文、PDF
参见API;
五、UIBezierPath绘图
UIBezierPath 既可以设置路径,也可以绘画。
参见UIBezierPath!
代码工具 PaintCode
推荐一个代码工具 PaintCode,可以生成相应的 Core Graphics 代码,大大加快我们的开发效率