在iOS开发中,绘制图形和图像处理是非常重要的一部分。Core Graphics框架提供了一系列强大的API,用于创建高质量的图形和进行复杂的图像处理。在本文中,我们将深入探讨Core Graphics框架,并提供一些实用的代码示例。
什么是Core Graphics框架?
Core Graphics框架是一个基于C语言的框架,它提供了一种绘制2D图形的方式。它支持多种颜色空间、路径绘制和渐变等功能,并且可以用于创建PDF和PostScript文件,以及直接在屏幕上呈现图形。
Core Graphics框架也称为Quartz2D,其主要功能包括:
- 绘制线条、圆形、矩形和其他形状
- 渲染图像和文本
- 创建阴影和渐变效果
- 裁剪图形
- 处理PDF
- 屏幕截图
- 缩放裁剪图片
- 绘制统计图(折线图、柱状图、扇形图)
Core Graphics框架的基本概念
在开始使用Core Graphics框架之前,需要了解一些基本概念:
图形上下文(Graphics Context)
图形上下文是一组参数,用于指定如何绘制图形。它包含了当前的绘图状态、绘图目标和其他必要的信息。你会发现很多Core Graphics函数都需要一个CGContextRef类型的参数,这就是图形上下文。
使用自己创建的上下文性能效率不高,通常创建一个UIView的子类以获得它的上下文。在UIView中调用 drawRect:
方法时,会自动准备好一个图形上下文,可以通过调用 UIGraphicsGetCurrentContext()
来获取。开发者只能获取,不能自己重新创建,在该图层上下文中绘制的图形,最终会通过CALayer显示出来。 因为它是运行期间绘制图片,我们可以动态的做一些额外的操作。
路径(Path)
路径是由线条和曲线构成的集合,描述了一种特定的形状。通过绘制路径,你可以创建自定义形状,例如圆形、矩形、多边形和任意线条。绘图步骤通常包括以下几个步骤:
- 获取图形上下文
- 描述绘画内容
a. 创建图形路径
b. 创建图形起始点
c. 添加图形的终点 - 把绘画内容添加到图形上下文
- 设置图形上下文的状态(颜色和线条宽度等)
- 渲染图形上下文
上下文状态(Graphics State)
上下文状态是指图形上下文的所有属性,如颜色、线条宽度、字体、阴影等。当你更改上下文状态时,所有后续的绘图操作都将受到影响。因此,在更改上下文状态之前,最好保留原有的状态,以便在需要时进行恢复。
颜色空间(Color Space)
颜色空间是一种映射颜色值的数学模型。它定义了颜色由哪些元素组成,以及如何将这些元素转换为设备上的实际颜色。Core Graphics提供了许多不同的颜色空间,如RGB、CMYK、灰度和带Alpha通道的空间。
渐变(Gradient)
渐变是一种从一个颜色到另一个颜色过渡的效果。它可以是线性的或径向的,并且可以在一段距离内平滑地过渡。Core Graphics提供了许多不同的渐变类型,如线性渐变、径向渐变和轴向渐变。
Core Graphics框架的代码示例
下面是一些使用Core Graphics框架进行图形绘制的代码示例,其中包括画线、画圆和渐变等。
画线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// MyView.h
#import <UIKit/UIKit.h>
@interface MyView : UIView
@end
// MyView.m
#import "MyView.h"
@implementation MyView
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置起点和终点
CGPoint startPoint = CGPointMake(50, 50);
CGPoint endPoint = CGPointMake(350, 50);
// 设置线条颜色和宽度
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineWidth(context, 3.0);
// 创建路径并添加线条
CGContextBeginPath(context);
CGContextMoveToPoint(context, startPoint.x, startPoint.y);
CGContextAddLineToPoint(context, endPoint.x, endPoint.y);
// 绘制路径
CGContextStrokePath(context);
}
@end
// testViewController.m
#import "testViewController.h"
@interface testViewController ()
@end
@implementation testViewController
- (void)viewDidLoad {
[super viewDidLoad];
MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(10, 100, 350, 350)];
myView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:myView];
}
@end
画圆
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置圆心和半径
CGPoint center = CGPointMake(100, 100);
CGFloat radius = 50.0;
// 创建路径并添加圆形
CGContextBeginPath(context);
CGContextAddArc(context, center.x, center.y, radius, 0.0, M_PI * 2, YES);
// 设置填充颜色和绘制路径
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextFillPath(context);
}
渐变
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置起点和终点
CGPoint startPoint = CGPointMake(50, 50);
CGPoint endPoint = CGPointMake(350, 50);
// 创建颜色空间
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// 设置渐变色的颜色数组和位置数组
NSArray *colors = @[(id)[UIColor redColor].CGColor, (id)[UIColor greenColor].CGColor];
CGFloat locations[] = {0.0, 1.0};
// 创建渐变对象并设置路径
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
CGContextBeginPath(context);
CGContextMoveToPoint(context, startPoint.x, startPoint.y);
CGContextAddLineToPoint(context, endPoint.x, endPoint.y);
// 将渐变应用于路径
CGContextSaveGState(context);
CGContextReplacePathWithStrokedPath(context);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
// 释放内存
CGColorSpaceRelease(colorSpace);
CGGradientRelease(gradient);
}
绘制五角星
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置起点和半径
CGPoint center = CGPointMake(100, 100);
CGFloat radius = 50.0;
// 计算五个顶点坐标
CGPoint points[5];
CGFloat angle = -M_PI_2;
CGFloat delta = 2 * M_PI / 5.0;
for (int i = 0; i < 5; i++) {
CGFloat x = center.x + cos(angle) * radius;
CGFloat y = center.y + sin(angle) * radius;
points[i] = CGPointMake(x, y);
angle += delta;
}
// 创建路径并添加五角星
CGContextBeginPath(context);
CGContextMoveToPoint(context, points[0].x, points[0].y);
for (int i = 1; i < 5; i++) {
CGContextAddLineToPoint(context, points[i].x, points[i].y);
}
CGContextClosePath(context);
// 设置填充颜色和绘制路径
CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
CGContextFillPath(context);
}
绘制心形图案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置起点和大小
CGPoint center = CGPointMake(100, 100);
CGSize size = CGSizeMake(80, 80);
// 创建路径并添加心形
CGContextBeginPath(context);
CGContextMoveToPoint(context, center.x, center.y - size.height / 4);
CGContextAddCurveToPoint(context, center.x + size.width / 2, center.y - size.height / 2,
center.x + size.width / 2, center.y + size.height / 4,
center.x, center.y + size.height / 2);
CGContextAddCurveToPoint(context, center.x - size.width / 2, center.y + size.height / 4,
center.x - size.width / 2, center.y - size.height / 2,
center.x, center.y - size.height / 4);
CGContextClosePath(context);
// 设置填充颜色和绘制路径
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillPath(context);
}
给图片加水印
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 加载图像并绘制
UIImage *image = [UIImage imageNamed:@"image.png"];
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
// 绘制文本
NSString *text = @"这是图片上的水印文字";
NSDictionary *attributes = @{NSFontAttributeName: [UIFont systemFontOfSize:20.0],
NSForegroundColorAttributeName: [UIColor whiteColor]};
CGSize textSize = [text sizeWithAttributes:attributes];
CGRect textRect = CGRectMake((image.size.width - textSize.width) / 2.0, image.size.height - textSize.height - 10.0, textSize.width, textSize.height);
[text drawInRect:textRect withAttributes:attributes];
}
创建阴影
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置起点和大小
CGPoint center = CGPointMake(100, 100);
CGSize size = CGSizeMake(80, 80);
// 创建路径并添加矩形
CGContextBeginPath(context);
CGContextAddRect(context, CGRectMake(center.x - size.width / 2, center.y - size.height / 2, size.width, size.height));
// 设置阴影属性并绘制路径
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(5, 5), 10.0, [UIColor grayColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextFillPath(context);
CGContextRestoreGState(context);
}
绘制PDF文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- (void)drawRect:(CGRect)rect {
// 设置页面大小和边距
CGRect pageRect = CGRectMake(0, 0, 612, 792); // Letter size with 1 inch margin
UIEdgeInsets pageMargins = UIEdgeInsetsMake(72, 72, 72, 72); // 1 inch margin
// 创建PDF文档
NSString *pdfPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"MyPDF.pdf"];
CFURLRef pdfURL = (__bridge CFURLRef)[NSURL fileURLWithPath:pdfPath];
CGContextRef context = CGPDFContextCreateWithURL(pdfURL, &pageRect, NULL);
// 开始页面
CGContextBeginPage(context, &pageRect);
// 绘制内容
NSString *text = @"Hello World!";
UIFont *font = [UIFont systemFontOfSize:20.0];
NSDictionary *attributes = @{NSFontAttributeName: font};
CGSize textSize = [text sizeWithAttributes:attributes];
CGRect textRect = CGRectMake(pageMargins.left, pageMargins.top, pageRect.size.width - pageMargins.left - pageMargins.right,
pageRect.size.height - pageMargins.top - pageMargins.bottom);
[text drawInRect:textRect withAttributes:attributes];
// 结束页面并释放内存
CGContextEndPage(context);
CGContextRelease(context);
}
屏幕截图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (void)screenshot {
// 开启一个位图上下文
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);
// 获取位图上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 把屏幕上的图层渲染到图形上下文
[self.view.layer renderInContext:ctx];
// 从位图上下文获取图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
// 存储图片
// image转data,compressionQuality:图片质量0-1
NSData *data = UIImageJPEGRepresentation(image,1);
[data writeToFile:@"/Users/user/Desktop/img.png" atomically:YES];
}
缩放裁剪图片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 加载UIImage并绘制
UIImage *image = [UIImage imageNamed:@"image.png"];
// [image drawInRect:self.bounds];
// 计算圆形路径并设置裁剪区域
CGFloat radius = 50.0;
CGFloat centerX = 100;
CGFloat centerY = 100;
CGContextAddArc(context, centerX, centerY, radius, 0, M_PI*2, YES);
CGContextClip(context);
// 绘制裁剪后的图像
CGRect imageRect = CGRectMake(centerX - radius, centerY - radius, radius * 2, radius * 2);
[image drawInRect:imageRect];
}
折线图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置坐标轴范围
CGFloat padding = 20.0;
CGFloat xStart = padding + 35.0; // x轴起点
CGFloat yStart = self.bounds.size.height - padding - 30.0; // y轴起点
CGFloat xEnd = self.bounds.size.width - padding;
CGFloat yEnd = padding; // y轴终点
CGFloat xRange = xEnd - xStart;
CGFloat yRange = yStart - yEnd;
NSArray *dataArray = @[@"23", @"35", @"5", @"56",@"25",];
int maxValue = 100;
// 绘制x轴刻度
CGFloat xSpacing = xRange / (dataArray.count - 1);
for (int i = 0; i < dataArray.count; i++) {
NSString *text = [NSString stringWithFormat:@"%d", i];
CGSize textSize = [text sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0]}];
CGPoint textPoint = CGPointMake(xStart + xSpacing * i - textSize.width / 2.0, yStart + 5.0);
[text drawAtPoint:textPoint withAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0]}];
}
// 绘制y轴刻度
CGFloat ySpacing = yRange / 5.0;
for (int i = 0; i <= 5; i++) {
NSString *text = [NSString stringWithFormat:@"%.0f", maxValue / 5.0 * i];
CGSize textSize = [text sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0]}];
CGPoint textPoint = CGPointMake(xStart - textSize.width - 5.0, yStart - ySpacing * i - textSize.height / 2.0);
[text drawAtPoint:textPoint withAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0]}];
CGContextSetStrokeColorWithColor(context, [UIColor lightGrayColor].CGColor);
CGContextSetLineWidth(context, 1.0);
CGContextMoveToPoint(context, xStart, yStart - ySpacing * i);
CGContextAddLineToPoint(context, xEnd, yStart - ySpacing * i);
CGContextStrokePath(context);
}
// 绘制坐标轴
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextMoveToPoint(context, xEnd, yStart);
CGContextAddLineToPoint(context, xStart, yStart);
CGContextAddLineToPoint(context, xStart, yEnd);
CGContextStrokePath(context);
// 绘制数据线
CGContextSetLineWidth(context, 1.0);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextBeginPath(context);
for (int i = 0; i < dataArray.count; i++) {
CGFloat xValue = xStart + xRange / (dataArray.count - 1) * i;
CGFloat yValue = yStart - yRange / maxValue * [dataArray[i] floatValue];
if (i == 0) {
CGContextMoveToPoint(context, xValue, yValue);
} else {
CGContextAddLineToPoint(context, xValue, yValue);
}
// 标注数据点数值
NSString *valueText = [NSString stringWithFormat:@"%d", [dataArray[i] intValue]];
CGSize valueSize = [valueText sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0]}];
CGPoint valuePoint = CGPointMake(xValue - valueSize.width / 2.0, yValue - valueSize.height - 3.0);
[valueText drawAtPoint:valuePoint withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12.0], NSForegroundColorAttributeName: [UIColor blueColor]}];
}
CGContextStrokePath(context);
}
柱状图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置坐标轴范围
CGFloat padding = 20.0;
CGFloat xStart = padding + 35.0; // x轴起点
CGFloat yStart = self.bounds.size.height - padding - 30.0; // y轴起点
CGFloat xEnd = self.bounds.size.width - padding;
CGFloat yEnd = padding; // y轴终点
CGFloat xRange = xEnd - xStart;
CGFloat yRange = yStart - yEnd;
NSArray *dataArray = @[@"23", @"35", @"5", @"56",@"25",];
int maxValue = 100;
// 绘制x轴刻度
CGFloat xSpacing = (xRange - padding * 2) / (dataArray.count - 1);
for (int i = 0; i < dataArray.count; i++) {
NSString *text = [NSString stringWithFormat:@"%d", i];
CGSize textSize = [text sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0]}];
CGPoint textPoint = CGPointMake(padding + xStart + xSpacing * i - textSize.width / 2.0, yStart + 5.0);
[text drawAtPoint:textPoint withAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0]}];
}
// 绘制y轴刻度
CGFloat ySpacing = yRange / 5.0;
for (int i = 0; i <= 5; i++) {
NSString *text = [NSString stringWithFormat:@"%.0f", maxValue / 5.0 * i];
CGSize textSize = [text sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0]}];
CGPoint textPoint = CGPointMake(xStart - textSize.width - 5.0, yStart - ySpacing * i - textSize.height / 2.0);
[text drawAtPoint:textPoint withAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0]}];
CGContextSetStrokeColorWithColor(context, [UIColor lightGrayColor].CGColor);
CGContextSetLineWidth(context, 1.0);
CGContextMoveToPoint(context, xStart, yStart - ySpacing * i);
CGContextAddLineToPoint(context, xEnd, yStart - ySpacing * i);
CGContextStrokePath(context);
}
// 绘制坐标轴
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextMoveToPoint(context, xStart, yStart);
CGContextAddLineToPoint(context, xEnd, yStart);
CGContextMoveToPoint(context, xStart, yEnd);
CGContextAddLineToPoint(context, xStart, yStart);
CGContextStrokePath(context);
// 绘制柱子
CGFloat barWidth = xSpacing / 2.0;
for (int i = 0; i < dataArray.count; i++) {
CGFloat xValue = padding + xStart + xSpacing * i - barWidth / 2.0;
CGFloat yValue = yStart - yRange / maxValue * [dataArray[i] floatValue];
CGFloat barHeight = yStart - yValue;
CGRect barRect = CGRectMake(xValue, yValue, barWidth, barHeight);
CGContextAddRect(context, barRect);
// 标注柱子数值
NSString *valueText = [NSString stringWithFormat:@"%d", [dataArray[i] intValue]];
CGSize valueSize = [valueText sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0]}];
CGPoint valuePoint = CGPointMake(xValue + barWidth / 2.0 - valueSize.width / 2.0, yValue - valueSize.height - 3.0);
[valueText drawAtPoint:valuePoint withAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0], NSForegroundColorAttributeName: [UIColor blueColor]}];
}
CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
CGContextFillPath(context);
}
扇形图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
// 设置圆心和半径
CGPoint center = CGPointMake(self.bounds.size.width / 2.0, self.bounds.size.height / 2.0);
CGFloat radius = MIN(center.x, center.y) - 20.0;
NSArray *dataArray = @[@"23", @"35", @"5", @"12",@"25",];
int totalValue = 100;
// 绘制扇形
CGFloat startAngle = 0.0;
for (int i = 0; i < dataArray.count; i++) {
CGFloat endAngle = startAngle + [dataArray[i] floatValue] / totalValue * M_PI * 2.0;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
[path addLineToPoint:center];
[path closePath];
UIColor *color = [UIColor colorWithHue:(CGFloat)i / dataArray.count saturation:1.0 brightness:1.0 alpha:1.0];
[color setFill];
[path fill];
// 标注扇形数值
NSString *valueText = [NSString stringWithFormat:@"%.1f%%", [dataArray[i] floatValue] / totalValue * 100.0];
CGSize valueSize = [valueText sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0]}];
CGFloat valueAngle = startAngle + (endAngle - startAngle) / 2.0;
CGFloat valueRadius = radius + 20.0;
CGPoint valuePoint = CGPointMake(center.x + cos(valueAngle) * valueRadius - valueSize.width / 2.0,
center.y + sin(valueAngle) * valueRadius - valueSize.height / 2.0);
[valueText drawAtPoint:valuePoint withAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0], NSForegroundColorAttributeName: color}];
startAngle = endAngle;
}
}