iOS-Quartz2D的基本使用

文章主要包含以下几个方面:

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,就可以将相同的图像绘制到不同的目标上。

1672235-859e483d82e92159.png
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);
    
}

1672235-a834911dc4f373d6.png
002.png
三. 饼图绘制

了解了直线和曲线的绘制之后,我们再来看下稍微复杂的饼图的绘制

1672235-4b67125d6f233c13.png
003.png

1.首先我们先绘制扇形的一部分,具体代码如下:

- (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];
}

运行,看到效果如下:


1672235-d4883ababdc5b413.png
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所示:


1672235-c27ff2dbf3ea78cc.png
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;
}

效果如下:

1672235-89725a3608447154.png
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;
    
}

效果图如下:

1672235-77d1eecd3c122852.png
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];
}

效果如下所示:

1672235-0e4a2a349cf4bb1f.gif
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下载地址.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值