文 || 張贺
Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统
Quartz 2D能完成的工作:
- 绘制图形 : 线条/三角形/矩形/圆/弧等
- 绘制文字
- 绘制/生成图片(图像)
- 读取/生成PDF
- 截图/裁剪图片
- 自定义UI控件(重要)
图形上下文
图形上下文(Graphics Context):是一个CGContextRef类型的数据
图形上下文的作用:
- 保存绘图信息、绘图状态
- 决定绘制的输出目标(绘制到什么地方去?)
(输出目标可以是PDF文件、Bitmap或者显示器的窗口上)
相同的一套绘图序列,指定不同的Graphics Context,就可将相同的图像绘制到不同的目标上
Quartz2D提供了以下几种类型的Graphics Context:
- Bitmap Graphics Context
- PDF Graphics Context
- Window Graphics Context
- Layer Graphics Context
- Printer Graphics Context
drawRect:方法
-
如何利用Quartz2D绘制东西到view上?
首先,得有图形上下文,因为它能保存绘图信息,并且决定着绘制到什么地方去
其次,那个图形上下文必须跟view相关联,才能将内容绘制到view上面 -
如何利用Quartz2D自定义view?(自定义UI控件)
1.新建一个类,继承自UIView
2.实现- (void)drawRect:(CGRect)rect方法,然后在这个方法中取得跟当前view相关联的图形上下文
3.绘制相应的图形内容
4.利用图形上下文将绘制的所有内容渲染显示到view上面 -
为什么要实现drawRect:方法才能绘图到view上?
因为在drawRect:方法中才能取得跟view相关联的图形上下文 -
drawRect:方法在什么时候被调用?
当view第一次显示到屏幕上时(被加到UIWindow上显示出来)
调用view的setNeedsDisplay(重绘)或者setNeedsDisplayInRect:时
注意:调用setNeedsDisplay方法并不会立马调用drawRect:方法,只是设了一个标志,当屏幕下一次刷新的时候才去调用drawRect:方法。(屏幕每秒刷新60次)
画直线
/**
* 在drawRect:方法中系统已经帮你创建好了一个跟view相关联的上下文(layer上下文),只需要获取就好了
* 作用:专门用来绘图
* 什么时候调用:当view显示的时候自动调用
* @param rect:当前view的bounds
*
*/
- (void)drawRect:(CGRect)rect {
//1.获取图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//上下文的状态(线的粗细、颜色、链接样式等)
//设置线的粗细
CGContextSetLineWidth(ctx, 10);
//设置链接点的样式
/**
typedef CF_ENUM(int32_t, CGLineJoin) {
kCGLineJoinMiter,
kCGLineJoinRound,
kCGLineJoinBevel
};
*/
CGContextSetLineJoin(ctx, kCGLineJoinRound);
//设置线的顶角样式
/**
typedef CF_ENUM(int32_t, CGLineCap) {
kCGLineCapButt,
kCGLineCapRound,
kCGLineCapSquare
};
*/
CGContextSetLineCap(ctx, kCGLineCapRound);
//设置颜色(注意:这里设置颜色必须跟渲染方式一致,否则设置不上颜色)
// [[UIColor redColor]setFill]; //渲染方式为Fill
// [[UIColor redColor]setStroke]; //渲染方式为Stroke
[[UIColor redColor]set]; //两种渲染方式均可,推荐使用
//2. 绘制路径
UIBezierPath *path = [UIBezierPath bezierPath];
//2.1 设置起点
[path moveToPoint:CGPointMake(50, 250)];
//2.2 添加一根线到终点
[path addLineToPoint:CGPointMake(250, 50)];
//画第二条直线(新起点)
[path moveToPoint:CGPointMake(100, 250)];
[path addLineToPoint:CGPointMake(250, 100)];
//把上一条线的终点当做起点来画第二条线
[path addLineToPoint:CGPointMake(250, 250)];
//3.把绘制的内容添加到上下文中
// UIBezierPath:UIKit框架 ---> CGPathRef:CoreGraphics框架
CGContextAddPath(ctx, path.CGPath);
//4.把上下文的内容显示到view上(渲染到view的layer上)渲染的方式有两种Stroke(描边)、Fill(填充)
CGContextStrokePath(ctx);
}
画曲线
//画曲线
- (void)drawQuadCurve{
//1.获取图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.绘制图形
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(50, 250)];
//添加一条曲线
[path addQuadCurveToPoint:CGPointMake(250, 250) controlPoint:CGPointMake(50, 50)];
//3.把绘制的内容添加到上下文中
CGContextAddPath(ctx, path.CGPath);
//4.把上下文的内容渲染到view上
CGContextStrokePath(ctx);
}
图片来自苹果官方文档
画矩形
- (void)drawRect:(CGRect)rect {
//1.获取图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//设置颜色
[[UIColor redColor]set];
//2.绘制路径
//画矩形
// UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 100, 100)];
//画圆角矩形
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 100, 100) cornerRadius:50];
//3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
//4.把上下文渲染到view上
//描边
// CGContextStrokePath(ctx);
//填充
CGContextFillPath(ctx);
}
画椭圆
- (void)drawRect:(CGRect)rect {
//画椭圆
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 100, 50)];
//画矩形
// UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(50, 50, 100, 50)];
//画线
[path moveToPoint:CGPointMake(50, 250)];
[path addLineToPoint:CGPointMake(250, 250)];
//设置颜色
[[UIColor redColor]set];
//设置线条的粗细
[path setLineWidth:10];
//[path stroke] 底层:
//1.获取图形上下文 -> 2.绘制路径 -> 3.把路径添加到上下文 -> 4.把上下文渲染到view上
[path stroke];
//填充
// [path fill];
}
画弧
- (void)drawRect:(CGRect)rect {
//画弧
//ArcCenter:弧所在圆的圆心
//radius:弧所在圆的半径
//startAngle:起始角度
//endAngle:结束角度
//clockwise: YES:顺时针 NO:逆时针
//不能直接使用self.center :因为self.center坐标是相对于他的父控件
CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
CGFloat radius = rect.size.width * 0.5 - 10;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:M_PI_4 clockwise:YES];
[path stroke];
}
画扇形
- (void)drawRect:(CGRect)rect {
//画弧
//ArcCenter:弧所在圆的圆心
//radius:弧所在圆的半径
//startAngle:起始角度
//endAngle:结束角度
//clockwise: YES:顺时针 NO:逆时针
//不能直接使用self.center :因为self.center坐标是相对于他的父控件
CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
CGFloat radius = rect.size.width * 0.5 - 10;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:M_PI_4 clockwise:YES];
//画扇形
//添加一根线到圆心
[path addLineToPoint:center];
//关闭路径:从路径终点添加一根线到路径的起点
[path closePath];
//填充之前,会自动关闭路径
// [path fill];
[path stroke];
}
画饼图
#import "PieView.h"
@implementation PieView
- (void)drawRect:(CGRect)rect {
NSArray *dataArray = @[@15,@20,@5,@10,@50];
CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
CGFloat redius = rect.size.width * 0.5 - 10;
CGFloat startA = 0;
CGFloat angle = 0;
CGFloat endA = 0;
for (NSNumber *num in dataArray) {
startA = endA;
angle = num.intValue / 100.0 * M_PI * 2;
endA = startA + angle;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:redius startAngle:startA endAngle:endA clockwise:YES];
//随机颜色
[[self randomColor]set];
//弧的终点向圆心画一条线
[path addLineToPoint:center];
//填充
[path fill];
}
}
//随机颜色
- (UIColor *)randomColor{
CGFloat r = arc4random_uniform(256) / 255.0;
CGFloat g = arc4random_uniform(256) / 255.0;
CGFloat b = arc4random_uniform(256) / 255.0;
return [UIColor colorWithRed:r green:g blue:b alpha:1];
}
@end
饼图
UIKit绘图
画文字
- (void)drawRect:(CGRect)rect {
// Drawing code
NSString *str = @"Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统";
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
//设置文字大小
dict[NSFontAttributeName] = [UIFont systemFontOfSize:20];
//设置文字颜色
dict[NSForegroundColorAttributeName] = [UIColor greenColor];
//设置描边宽度
dict[NSStrokeWidthAttributeName] = @2;
//设置描边颜色
dict[NSStrokeColorAttributeName] = [UIColor blueColor];
//设置阴影
NSShadow *shadow = [[NSShadow alloc] init];
//设置阴影的便宜量
shadow.shadowOffset = CGSizeMake(10, 10);
//设置阴影颜色
shadow.shadowColor = [UIColor greenColor];
//设置阴影模糊程序
shadow.shadowBlurRadius = 1;
dict[NSShadowAttributeName] = shadow;
/**
AtPoint:文字所画的位置
withAttributes:描述文字的属性.
*/
//不会自动换行
[str drawAtPoint:CGPointZero withAttributes:dict];
//会自动换行.
[str drawInRect:self.bounds withAttributes:dict];
}
画图片
- (void)drawRect:(CGRect)rect {
// Drawing code
//1.加载图片
UIImage *image = [UIImage imageNamed:@"agt"];
//绘制出来的图片,是保持原来图片大小
[image drawAtPoint:CGPointZero];
//把图片填充到这个rect当中.
[image drawInRect:rect];
//添加裁剪区域 .把超区裁剪区域以外都裁剪掉
UIRectClip(CGRectMake(0, 0, 50, 50));
//平铺
[image drawAsPatternInRect:self.bounds];
//快速的画出一个矩形
[[UIColor blueColor] set];
UIRectFill(CGRectMake(10, 10, 100, 100));
}
上下文状态栈
- (void)drawRect:(CGRect)rect {
// Drawing code
//1.获取图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.描述路径
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 100)];
[path addLineToPoint:CGPointMake(150, 100)];
/*******保存当前上下文的状态到上下文状态栈*********/
CGContextSaveGState(ctx);
//修改状态
CGContextSetLineWidth(ctx, 10);
[[UIColor redColor]set];
//3.将路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
//4.将上下文渲染到view上
CGContextStrokePath(ctx);
//2.描述路径
UIBezierPath *path2 = [UIBezierPath bezierPath];
[path2 moveToPoint:CGPointMake(100, 10)];
[path2 addLineToPoint:CGPointMake(100, 150)];
//修改状态
// CGContextSetLineWidth(ctx, 1);
// [[UIColor blackColor]set];
/*******从上下文状态栈中取出最上面的状态*********/
CGContextRestoreGState(ctx);
//3.将路径添加到上下文
CGContextAddPath(ctx, path2.CGPath);
//4.将上下文渲染到view上
CGContextStrokePath(ctx);
}
@end
绘制不同的状态
图形上下文的矩阵操作
- (void)drawRect:(CGRect)rect {
// Drawing code
//1.获取图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.绘制路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 50)];
//把路径添加到上下文中之前可以做一些矩阵操作
//平移
CGContextTranslateCTM(ctx, 100, 100);
//旋转
CGContextRotateCTM(ctx, M_PI_4);
//缩放
CGContextScaleCTM(ctx, 1.5, 1.5);
//3.把路径添加到图形上下文中
CGContextAddPath(ctx, path.CGPath);
//4.把上下文渲染到view上
CGContextFillPath(ctx);
}
给图片加水印
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageV;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//0.加载图片
UIImage *image = [UIImage imageNamed:@"123"];
//1.开启一个跟图片大小一样的上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
//2.把图片绘制到上下文中
[image drawAtPoint:CGPointZero];
//3.把文字绘制到上下文中
NSString *watermark = @"張贺";
[watermark drawAtPoint:CGPointMake(10, 20) withAttributes:nil];
//4.从上下文中生成一张新的图片(把上下文中绘制的所有内容,生成一张新的图片)
UIImage *waterMarkImage = UIGraphicsGetImageFromCurrentImageContext();
//5.关闭上下文
UIGraphicsEndImageContext();
self.imageV.image = waterMarkImage;
}
@end
给图片加水印
圆形图片裁剪
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageV;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//0.加载图片
UIImage *image = [UIImage imageNamed:@"icon"];
//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.从上下文中取出图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//5.关闭上下文
UIGraphicsEndImageContext();
self.imageV.image = newImage;
}
@end
裁剪前
裁剪后
带边框的圆形图片裁剪
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageV;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//0.加载图片
UIImage *image = [UIImage imageNamed:@"icon"];
//1.确定边框宽度
CGFloat border = 10;
//2.开启一个上下文
CGSize size = CGSizeMake(image.size.width + 2 * border, image.size.height + 2 * border);
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
//3.绘制大圆,显示出来
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, size.width, size.height)];
[[UIColor redColor]set];
[path fill];
//4.绘制一个小圆,把小圆设置成裁剪区域
UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(border, border, image.size.width, image.size.height)];
[clipPath addClip];
//5.把图片绘制到上下文中
[image drawAtPoint:CGPointMake(border, border)];
//6.从上下文中取出图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//7.关闭上下文
UIGraphicsEndImageContext();
self.imageV.image = newImage;
}
带边框的图片裁剪
截屏
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//把控制器的view生成一张图片
//1.开启一个位图上下文(大小和控制器的view大小一样)
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);
//2.把控制器的view绘制到上下文中
//想要把view上面的东西绘制到上下文当中,必须使用渲染的方式
CGContextRef ctx = UIGraphicsGetCurrentContext();
[self.view.layer renderInContext:ctx];
//3.从上下文中生成一张图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//4.关闭上线文
UIGraphicsEndImageContext();
//把生成的图片写入到桌面(文件方式进行传输:二进制流NSData)
//把图片转成二进制流
//png 默认最高质量
//UIImagePNGRepresentation(newImage)
//jpg 第二个参数是截图质量
NSData *data = UIImageJPEGRepresentation(newImage, 1);
[data writeToFile:@"/Users/zhanghe/Desktop/截屏.jpg" atomically:YES];
}
当然,在实际开发中我们不需要自己去画折线图、饼图、各种统计图... ...
一个很好的第三方库 Charts 是一个简单、面向对象、为设计者和开发者准备的图表绘制工具库
网友评论