二维绘图引擎Quartz 2D

作者: 張贺 | 来源:发表于2016-08-19 23:08 被阅读205次
图片来自500px

文 || 張贺

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 是一个简单、面向对象、为设计者和开发者准备的图表绘制工具库

相关文章

  • Quartz 2D

    Quartz 2D简介 Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统 Quartz 2D能完成...

  • Quartz 2D绘图 (1)初识

    Quartz 2D介绍 什么是Quartz2D ?Quartz 2D是⼀个二维绘图引擎,同时支持iOS和Mac系统...

  • Quartz2D绘图演练

    1、什么是Quartz2D?二维的绘图引擎 Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统,用Q...

  • Quartz2D简单介绍

    一、什么是Quartz2D Quartz 2D是⼀个二维绘图引擎,同时支持iOS和Mac系统 Quartz 2D能...

  • Quartz2D?

    什么是Quartz2D? Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统 Quartz 2D能完...

  • iOS学习笔记(6):Quartz2D

    什么是Quartz2D Quartz 2D 是一个二维绘图引擎,同时支持iOS和Mac系统 Quartz 2D的作...

  • Quartz2D

    什么是Quartz2D? Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统 Quartz 2D能完...

  • iOS开发 Quartz 2D 初探之绘图方式

    一、前言(什么是 Quartz 2D) Quartz 2D 是一个二维绘图引擎,同时支持 iOS 和 Mac OS...

  • Quartz 2D

    Quartz 2D是一个二维绘图引擎。Quartz 2D的API是C语言,来自于CoreGraphics框架。没有...

  • 【Quartz2D】绘图引擎

    什么是Quartz2D Quartz 2D是一个二维绘图引擎,同时支持iOS和Mac系统Quartz 2D能完成的...

网友评论

本文标题:二维绘图引擎Quartz 2D

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