美文网首页
iOS-Quartz2D的基本使用

iOS-Quartz2D的基本使用

作者: 翀鹰精灵 | 来源:发表于2019-05-25 15:39 被阅读0次

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

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

    运行,看到效果如下:


    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下载地址.

    相关文章

      网友评论

          本文标题:iOS-Quartz2D的基本使用

          本文链接:https://www.haomeiwen.com/subject/deuoiqtx.html