文章主要包含以下几个方面:
1.Quartz2D简介
2.Quartz2D基本线条绘制(画线)
3.饼图绘制
4.实战-图片加水印
5.实战-图片裁剪
6.实战-屏幕截屏功能(全截屏)
7.实战-图片截屏
8.实战-图片擦除
......
一、Quartz2D简介
Quartz2D是一个二维的绘图引擎。这个库提供了很多框架,能实现很多功能,包括:
- 绘制图形:线条/三角形/矩形/圆/弧等
- 绘制文字
- 绘制/生成图片(图像)
- 读取/生成PDF
- 截图/裁剪图片
- 自定义UI控件
- ... ...
Quartz2D实例
- 涂鸦画板
- 手势解锁
- 报表:折线图/饼图/柱状图
Quartz2D在iOS开发中的价值
- 为了便于搭建美观的UI界面,iOS提供了UIKit框架,里面有各种各样的UI控件
* 1.UILabel :显示文字
* 2.UIImageView:显示图片
* 3.UIBtuton :同事显示图片和文字(能点击)
* 4....
- 利用UIKit框架提供的控件,拼拼凑凑,能搭建和实现一些简单、场景 的UI界面
- 但是,有些UI界面集齐复杂,而且比价个性,用普通的UI控件无法实现,这时可以利用 Quartz2D技术奖控件内部的结构画出来,自定义控件的样子.
- 其实,iOS中大部门控件的内容都是通过Quartz2D画出来的。
- 因此,Quartz2D在iOS开发中很重要的一个价值是:自定义view(自定义UI控件)
图形上下文
定义:图形上下文(Graphics Context):是一个CGContextRef类型的数据。
图形上下文的作用
* 1.保存绘制信息,绘制状态
* 2.决定绘制的输出目标(输出到什么地方?输出目标可以是PDF文件、Bitmap或者显示器的窗口上)
* 3.相同的一套绘图序列,指定不同的Graphics Context,就可以将相同的图像绘制到不同的目标上。
001.png
自定义view
* 1.如何利用Quartz2D自定义view?(自定义UI控件)
* 2.如何利用Quartz2D绘制东西到view上?
* 2.1 首先,得有图形上下文,因为它能保存绘图信息,并且决定着会知道什么地方去。
* 2.2 其次,图形上下文必须跟view关联,才能将内容绘制到view上
自定义view的步骤
* 1.新建一个类,集成自UIView
* 2.实现-(void)drawRect:(CGRect)rect方法,然后在这个方法中
* 3.取得当前view相关联的图形上下文。
* 4.绘制翔宇的图形内容
* 5.利用图形上下文绘制的所有内容渲染到view上面
二、Quartz2D基本线条绘制(画线)
1.验证-(void)drawRect:(CGRect)rect
方法什么时候执行?
① 首先我们需要自定义一个Quartz2DView,然后实现-(void)drawRect:(CGRect)rect
方法,代码如下:
/**
作用:专门用来绘图
什么时候调用?
@param rect rect
*/
- (void)drawRect:(CGRect)rect {
// Drawing code
NSLog(@"---%s",__func__);
}
② 在Main.storyboard
里面绘制一个view,继承自Quartz2DView
③ 在ViewController里面验证代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"---%s",__func__);
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"---%s",__func__);
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"---%s",__func__);
}
通过查看控制台可看到如下的结果:
2019-04-09 18:16:17.697061+0800 Quartz2DDemo[6212:703455] ----[ViewController viewDidLoad]
2019-04-09 18:16:17.697430+0800 Quartz2DDemo[6212:703455] ----[ViewController viewWillAppear:]
2019-04-09 18:16:17.702117+0800 Quartz2DDemo[6212:703455] ----[Quartz2DView drawRect:]
2019-04-09 18:16:17.715547+0800 Quartz2DDemo[6212:703455] ----[ViewController viewDidAppear:]
通过观察控制台的输出内容可发现,执行顺序如下:viewDidLoad
-->viewWillAppear
-->drawRect
-->viewDidAppear
。
2.绘图直线的核心代码如下
我们先来进行简单的绘图操作,先绘制直线和曲线,核心代码如下:
- (void)drawRect:(CGRect)rect {
// [self drawLine];
[self drawQuadCurvel];
}
#pragma mark -- <画直线>
- (void)drawLine {
// 在drawRect方法中,系统已经帮你创建一个跟view相关联的上下文(layer上下文),只需要取上下文就行了
// 1.获取上下文(获取,创建上下文,都以UIGraphics开头)
CGContextRef context = UIGraphicsGetCurrentContext();
// 2.绘制路径
UIBezierPath *path = [UIBezierPath bezierPath];
// 2.1设置起点
[path moveToPoint:CGPointMake(50, 200)];
// 2.2添加一根线到绘制的终点
[path addLineToPoint:CGPointMake(250, 50)];
// 设置线的宽度
CGContextSetLineWidth(context, 20);
// 设置线的链接样式
CGContextSetLineJoin(context, kCGLineJoinRound);
// 设置线的顶角样式
CGContextSetLineCap(context, kCGLineCapRound);
// 设置颜色
[[UIColor redColor]setStroke];
// //2.2.1 画第二条线
// [path moveToPoint:CGPointMake(100, 100)];
// [path addLineToPoint:CGPointMake(250, 100)];
// //2.2.2把上一条线的终点,作为第二条线的起点
// [path addLineToPoint:CGPointMake(250, 300)];
// 3.把绘制的内容保存到上下文中
// UIBezierPath:UIKit CGPathRef:CoreGraphics
CGContextAddPath(context, path.CGPath);
// 4.把上下文的内容显示到view上(渲染到view的layer上)
CGContextStrokePath(context);
}
#pragma mark -- <画曲线>
-(void)drawQuadCurvel {
//1.获取上下文
CGContextRef context = UIGraphicsGetCurrentContext();
//2.描述路径
UIBezierPath *path = [UIBezierPath bezierPath];
//画曲线
//2.1设置起点
[path moveToPoint:CGPointMake(50, 280)];
//2.2添加一根曲线到某一个点
[path addQuadCurveToPoint:CGPointMake(250, 280) controlPoint:CGPointMake(50, 50)];
//3.把路径添加到上下文
CGContextAddPath(context, path.CGPath);
//4.把上下文的内容渲染到view上
CGContextStrokePath(context);
}
002.png
三. 饼图绘制
了解了直线和曲线的绘制之后,我们再来看下稍微复杂的饼图的绘制
003.png1.首先我们先绘制扇形的一部分,具体代码如下:
- (void)drawRect:(CGRect)rect {
// Drawing code
NSArray *dataArray = @[@(25),@(25),@(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 = 25 / 100.0 * M_PI * 2;
CGFloat endA = startA + angle ;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:redius startAngle:startA endAngle:endA clockwise:YES];
[[UIColor redColor]set];
//添加一根线到圆心
[path addLineToPoint:center];
[path fill];
}
运行,看到效果如下:
004.png
按照这个思路,我们可继续绘制第二个和第三个扇形,核心代码如下:
- (void)drawRect:(CGRect)rect {
// Drawing code
NSArray *dataArray = @[@(25),@(25),@(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 = 25 / 100.0 * M_PI * 2;
CGFloat endA = startA + angle ;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:redius startAngle:startA endAngle:endA clockwise:YES];
[[UIColor redColor]set];
//添加一根线到圆心
[path addLineToPoint:center];
[path fill];
//绘制第二个扇形
startA = endA;
angle = 25 / 100.0 * M_PI * 2;
endA = startA + angle;
path = [UIBezierPath bezierPathWithArcCenter:center radius:redius startAngle:startA endAngle:endA clockwise:YES];
[[UIColor yellowColor]set];
//添加一根线到圆心
[path addLineToPoint:center];
[path fill];
//绘制第三个扇形
startA = endA;
angle = 50 / 100.0 * M_PI * 2;
endA = startA + angle;
path = [UIBezierPath bezierPathWithArcCenter:center radius:redius startAngle:startA endAngle:endA clockwise:YES];
[[UIColor blueColor]set];
//添加一根线到圆心
[path addLineToPoint:center];
[path fill];
}
效如图005所示:
005.png
此时,虽然我们绘制出来了这个扇形,但是我们发现绘制第一个扇形、第二个扇形、第三个扇形的代码重复性非常高,所以这里可通过for循环将上述代码优化为下:
- (void)drawRect:(CGRect)rect {
// Drawing code
NSArray *dataArray = @[@(25),@(25),@(50)];
// [self drawPie:rect];
CGPoint center = CGPointMake(self.bounds.size.width * 0.5, self.bounds.size.height * 0.5);
CGFloat redius = self.bounds.size.width * 0.5 - 10;
CGFloat startA = 0 ;
CGFloat angle = 0;
CGFloat endA = 0 ;
for (NSNumber *num in dataArray) {
startA = endA;
angle = num.integerValue / 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];
}
}
效果同图005所示。
四.实战-图片加水印
为防止别人直接盗用项目中的图片,一般图片上都会有水印,在图片上加水印的核心代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//0.加载图片
UIImage *image=[UIImage imageNamed:@"1.png"];
//1.开启一个跟图片d原始大小的上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
//2.把图片绘制到上下文中
[image drawAtPoint:CGPointZero];
//3.把文字绘制到上下文当中
NSString *str = @"水印效果";
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSFontAttributeName] = [UIFont systemFontOfSize:100];
dict[NSForegroundColorAttributeName] = [UIColor redColor];
[str drawAtPoint:CGPointMake(20, 20) withAttributes:dict];
//4.从上下文当中生成一张图片(把上下文中绘制的所有内容生成一张图片)
UIImage *waterImage = UIGraphicsGetImageFromCurrentImageContext();
//5.关闭上下文
UIGraphicsEndImageContext();
self.imageView.image = waterImage;
}
效果如下:
006.png五.实战-图片裁剪
图片裁剪在项目中也非常常用,一般是做圆角头像的居多,这里通过上下文绘制的图片也可实现裁剪,核心代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIImage *image = [UIImage imageNamed:@"1.png"];
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
[path addClip];
[image drawAtPoint:CGPointZero];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageView.image = newImage;
}
效果图如下:
007.png六.实战-屏幕截屏功能(全截屏)
截屏功能app中也会设计,其核心代码如下:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 1.开启一个图形上下文(跟当前控制器view一样大小的尺寸)
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);
// 2.把控制器的view绘制到上下文中
// 想要把UIView上面的东西绘制到上下文中,必须要使用渲染的方式
CGContextRef context = UIGraphicsGetCurrentContext();
// Tips:这里必须得使用渲染的方式,绘制的方式不可行
//[self.view.layer drawInContext:context];---效果:空白展示
[self.view.layer renderInContext:context];
// 3.从上下文当中生成一张图
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 4.关闭上下文
UIGraphicsEndImageContext();
// 5.把生成的图片写入到桌面(以文件的方式进行传输:二进制流NSData)
NSData * data = UIImageJPEGRepresentation(newImage, 1);
// 这里路径自己设置,为便于测试,这里我随便设置了个桌面路径
[data writeToFile:@"/Users/allison/Desktop/temp/newImage.png" atomically:YES];
}
效果如下所示:
008.gif七. 实战-图片截屏
图片截屏在app中也非常常见,其核心代码如下:
- (IBAction)panGesture:(UIPanGestureRecognizer *)sender {
// 判断手势状态
CGPoint currentPoint = [sender locationInView:self.imageView];
if (sender.state == UIGestureRecognizerStateBegan) {
self.statrPoint = currentPoint;
} else if (sender.state == UIGestureRecognizerStateChanged) {
CGFloat x = self.statrPoint.x;
CGFloat y = self.statrPoint.y;
CGFloat w = currentPoint.x - self.statrPoint.x;
CGFloat h = currentPoint.y - self.statrPoint.y;
CGRect rect = CGRectMake(x, y, w, h);
//添加一个UIView
self.coverView.frame = rect;
} else if (sender.state == UIGestureRecognizerStateEnded) {
// 把超过covr的frame以外的内容裁减掉
// 生成了一张图片,把原来的图片给替换掉
UIGraphicsBeginImageContextWithOptions(self.imageView.bounds.size, NO, 0);
//把ImageView绘制到上下文前,设置一个裁剪区域
UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:self.coverView.frame];
[clipPath addClip];
// 把当前的ImageView渲染到上下文当中
CGContextRef context = UIGraphicsGetCurrentContext();
[self.imageView.layer renderInContext:context];
// 从上下文当中生成一张图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
// 移除遮罩
[self.coverView removeFromSuperview];
self.imageView.image = newImage;
}
}
8.实战-图片擦除
图片擦除功能,一般在一些抽奖app中会有该效果,其核心代码如下:
- (void)pan:(UIPanGestureRecognizer *)pan {
//1.先确定擦除区域
// 获取当前手指的点
CGPoint currentPoint = [pan locationInView:self.imageView2];
CGFloat rectWH = 30;
CGFloat x = currentPoint.x - rectWH * 0.5;
CGFloat y = currentPoint.y - rectWH * 0.5;
CGRect rect = CGRectMake(x, y, rectWH, rectWH);
// 生成一张带有透明度的擦除区域图片
//2.开启上下文
UIGraphicsBeginImageContextWithOptions(self.imageView2.bounds.size, NO, 0);
//3.把UIImageView内容渲染到当前的上下文当中
CGContextRef context =UIGraphicsGetCurrentContext();
[self.imageView2.layer renderInContext:context];
// 4.擦除上下文当中指定区域
CGContextClearRect(context, rect);
// 5.从上下文当中取出图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 6.替换之前ImageView的图片
self.imageView2.image = newImage;
}
目前仅仅总结了以上几个功能,后序如果有更深入的了解,再持续更新!
Quartz2DDemo下载地址.
网友评论