23.Quartz2D总结

一.Quartz2D简介

Quartz2D是二维的绘图引擎(经包装的函数库,方便开发者使用。也就是说苹果帮我们封装了一套绘图的函数库),用Quartz2D写的同一份代码,既可以运行在iphone上又可以运行在mac上,可以跨平台开发,开发中比较常用的是截屏/裁剪/自定义UI控件,Quartz2D在iOS开发中的价值就是自定义UI控件.

二.绘图简介

1.绘图的步骤: 
    1.获取上下文 
        CG:表示这个类在CoreGraphics框架里  Ref:引用,目前学的上下文都跟UIGraphics有关,想获取图形上下文,首先敲UIGraphics。

    2.创建路径(描述路径) 
        一般开发中用贝塞尔路径,里面封装了很多东西,可以帮我画一些基本的线段,矩形,圆等等
    4.渲染上下文
        首先获取图形上下文,然后描述路径,把路径添加到上下文,渲染到视图,图形上下文相当于一个内存缓存区,在内存里面操作是最快的,比直接在界面操作快多了

3.当这个View要显示的时候才会调用drawRect绘制图形,viewDidLoad后才会调用,因为那时候还没显示视图

4.注意:drawRect不能手动调用,因为图形上下文我们自己创建不了,只能由系统帮我们创建,并且传递给我们.需要我们手动调用 [self setNeedsDisplay](重绘,系统会先创建与view相关联的上下文,然后再调用drawRect)

三.Quartz2D绘图演练

1.绘制直线

  • 最原始的绘图方式
// 1.获取图形上下文
// 目前我们所用的上下文都是以UIGraphics
// CGContextRef Ref:引用 CG:目前使用到的类型和函数 一般都是CG开头 CoreGraphics
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.描述路径
// 创建路径
CGMutablePathRef path = CGPathCreateMutable();
// 设置起点
// path:给哪个路径设置起点
CGPathMoveToPoint(path, NULL, 50, 50);
// 添加一根线到某个点
CGPathAddLineToPoint(path, NULL, 200, 200);
// 3.把路径添加到上下文
CGContextAddPath(ctx, path);
// 4.渲染上下文
CGContextStrokePath(ctx);
  • 绘直线第二种方式

    // 获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 描述路径
    // 设置起点
    CGContextMoveToPoint(ctx, 50, 50);
    CGContextAddLineToPoint(ctx, 200, 200);
    // 渲染上下文
    CGContextStrokePath(ctx);
    
  • 绘直线第三种方式

    // UIKit已经封装了一些绘图的功能
    // 贝瑟尔路径
    // 创建路径
    UIBezierPath *path = [UIBezierPath bezierPath];
    // 设置起点
    [path moveToPoint:CGPointMake(50, 50)];
    // 添加一根线到某个点
    [path addLineToPoint:CGPointMake(200, 200)];
    // 绘制路径
    [path stroke];
    

2.绘制曲线

  • 最原始的绘图方式
// 原生绘制方法
// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 描述路径
// 设置起点
CGContextMoveToPoint(ctx, 50, 50);
// cpx:控制点的x
CGContextAddQuadCurveToPoint(ctx, 150, 20, 250, 50);
// 渲染上下文
CGContextStrokePath(ctx);

3.绘制图形

  • 圆弧
// Center:圆心
// startAngle:弧度
// clockwise:YES:顺时针 NO:逆时针

// 扇形
CGPoint center = CGPointMake(125, 125);
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:100 startAngle:0 endAngle:M_PI_2 clockwise:YES];    
// 添加一根线到圆心
[path addLineToPoint:center];
// 封闭路径,关闭路径:从路径的终点到起点
[path closePath];
[path stroke];
// 填充:必须是一个完整的封闭路径,默认就会自动关闭路径
//    [path fill];
  • 饼状图
//随机绘制饼状图
- (NSArray *)arrRandom
{
    int totoal = 100;
    NSMutableArray *arrM = [NSMutableArray array];
    int temp = 0;
    for (int i = 0; i < arc4random_uniform(10) + 1; i++) {
        temp = arc4random_uniform(totoal) + 1;
// 100 1~100
// 随机出来的临时值等于总值,直接退出循环,因为已经把总数分配完毕,没必要在分配。
        [arrM addObject:@(temp)];
        // 解决方式:当随机出来的数等于总数直接退出循环。
        if (temp == totoal) {
            break;
        }
        totoal -= temp;
     }
//如果余下的值不为0的情况
    if (totoal) {
        [arrM addObject:@(totoal)];
    }
    return arrM;
}

- (void)drawRect:(CGRect)rect {
    NSArray *arr = [self arrRandom];
    CGFloat radius = rect.size.width * 0.5;
    CGPoint center = CGPointMake(radius, radius);
    CGFloat startA = 0;
    CGFloat angle = 0;
    CGFloat endA = 0;

    for (int i = 0; i < arr.count; i++) {
        startA = endA;
        angle = [arr[i] integerValue] / 100.0 * M_PI * 2;
        endA = startA + angle;
        UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];
        [path addLineToPoint:center];
        [[self colorRandom] set];
        [path fill];
    }
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //重新绘制
    [self setNeedsDisplay];
}
- (UIColor *)colorRandom
{
    // 0 ~ 255 / 255
    // OC:0 ~ 1
    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];
}
  • 柱状图
- (void)drawRect:(CGRect)rect {

    NSArray *arr = [self arrRandom];

    CGFloat x = 0;
    CGFloat y = 0;
    CGFloat w = 0;
    CGFloat h = 0;

    for (int i = 0 ; i < arr.count; i++) {

        w = rect.size.width / (2*arr.count - 1);
        x = 2 * w * i;
        h = [arr[i] floatValue] / 100.0 *rect.size.height;
        y = rect.size.height - h;
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, w, h)];
        [[self colorRandom] set];
        [path fill];
    }
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    [self setNeedsDisplay];
}

- (NSArray *)arrRandom {

    int total = 100;

    NSMutableArray *arrM = [NSMutableArray array];

    int temp = 0;

    for (int i = 0 ; i < arc4random_uniform(10) + 1; i++) {

        temp = arc4random_uniform(total) + 1;

        [arrM addObject:@(temp)];

        if (temp == total) {

            break;
        }
        total -= temp;
    }

    if (total) {

        [arrM addObject:@(total)];
    }

    return arrM;
}
- (UIColor *)colorRandom
{
    // 0 ~ 255 / 255
    // OC:0 ~ 1
    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];

}
  • 绘制文字
// 绘制文字
NSString *str = @"asfdsfsdf";
// 文字的起点
// Attributes:文本属性
NSMutableDictionary *textDict = [NSMutableDictionary dictionary];
// 设置文字颜色
textDict[NSForegroundColorAttributeName] = [UIColor redColor];

// 设置文字字体
textDict[NSFontAttributeName] = [UIFont systemFontOfSize:30];
// 设置文字的空心颜色和宽度
textDict[NSStrokeWidthAttributeName] = @3;
textDict[NSStrokeColorAttributeName] = [UIColor yellowColor];

// 创建阴影对象
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowColor = [UIColor greenColor];
shadow.shadowOffset = CGSizeMake(4, 4);
shadow.shadowBlurRadius = 3;
textDict[NSShadowAttributeName] = shadow;

// 富文本:给普通的文字添加颜色,字体大小
[str drawAtPoint:CGPointZero withAttributes:textDict]; //不会换行
  • 绘制图片
// 超出裁剪区域的内容全部裁剪掉
// 注意:裁剪必须放在绘制之前
UIRectClip(CGRectMake(0, 0, 50, 50));//裁剪图片

UIImage *image = [UIImage imageNamed:@"001"];
// 默认绘制的内容尺寸跟图片尺寸一样大
[image drawAtPoint:CGPointZero];
//绘制图片在一定区域内
//[image drawInRect:rect];
// 绘图(平铺图片)
//[image drawAsPatternInRect:rect];

4.绘图中的定时器

如果在绘图的时候需要用到定时器,通常使用CADisplayLink

//CADisplayLink:每次屏幕刷新的时候就会调用,屏幕一般一秒刷新60次
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(timeChange)];
// 添加主运行循环
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

- (void)timeChange
{
//注意:这个方法并不会马上调用drawRect,其实这个方法只是给当前控件添加刷新的标记,等下一次屏幕刷新的时候才会调用drawRect
    [self setNeedsDisplay];
}

5.矩阵操作(平移,缩放,旋转)

- (void)drawRect:(CGRect)rect {
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.描述路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-100, -50, 200, 100)];
[[UIColor redColor] set];
// 上下文矩阵操作
// 注意:矩阵操作必须要在添加路径之前
//  平移
CGContextTranslateCTM(ctx, 100, 50);
// 缩放
CGContextScaleCTM(ctx, 0.5, 0.5);
// 旋转
CGContextRotateCTM(ctx, M_PI_4);
// 3.把路径添加上下文
CGContextAddPath(ctx, path.CGPath);
[[UIColor redColor] set];
// 4.渲染上下文
CGContextFillPath(ctx);
}

四.Quartz2D应用

1.图片水印

// 加载图片
UIImage *image = [UIImage imageNamed:@"小黄人"];
// 0.获取上下文,之前的上下文都是在view的drawRect方法中获取(跟View相关联的上下文layer上下文)
// 目前我们需要绘制图片到新的图片上,因此需要用到位图上下文
// 怎么获取位图上下文,注意位图上下文的获取方式跟layer上下文不一样。位图上下文需要我们手动创建。
// 开启一个位图上下文,注意位图上下文跟view无关联,所以不需要在drawRect.
// size:位图上下文的尺寸(新图片的尺寸)
// opaque: 不透明度 YES:不透明 NO:透明,通常我们一般都弄透明的上下文
// scale:通常不需要缩放上下文,取值为0,表示不缩放
    UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
UIBezierPath *path =[UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 300, 300)];

[[UIColor redColor] set];
[path stroke];

// 1.绘制原生的图片
[image drawAtPoint:CGPointZero];
// 2.给原生的图片添加文字
NSString *str = @"小码哥";

// 创建字典属性
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSForegroundColorAttributeName] = [UIColor redColor];
dict[NSFontAttributeName] = [UIFont systemFontOfSize:20];
[str drawAtPoint:CGPointMake(200, 528) withAttributes:dict];

// 3.生成一张图片给我们,从上下文中获取图片
UIImage *imageWater = UIGraphicsGetImageFromCurrentImageContext();
// 4.关闭上下文
// UIGraphicsEndImageContext();

self.imageView.image = imageWater;

2.图片裁剪

+ (UIImage *)imageWithClipImage:(UIImage *)image borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)color
{
    // 图片的宽度和高度
    CGFloat imageWH = image.size.width;

    // 设置圆环的宽度
    CGFloat border = borderWidth;

    // 圆形的宽度和高度
    CGFloat ovalWH = imageWH + 2 * border;

    // 1.开启上下文
UIGraphicsBeginImageContextWithOptions(CGSizeMake(ovalWH, ovalWH), NO, 0);
    // 2.画大圆
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, ovalWH, ovalWH)];
    [color set];
    [path fill];
    // 3.设置裁剪区域
    UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(border, border, imageWH, imageWH)];
    [clipPath addClip];
    // 4.绘制图片
    [image drawAtPoint:CGPointMake(border, border)];
    // 5.获取图片
    UIImage *clipImage = UIGraphicsGetImageFromCurrentImageContext();
    // 6.关闭上下文
    UIGraphicsEndImageContext();
    return clipImage;
}

3.图片截取

//根据手指移动截取任意大小的图片
//1.记录开始节点
//2.添加指定区域截取图片
//3.裁剪图片

// 给控制器的view添加一个pan手势
//记录开始节点
@property (nonatomic, assign) CGPoint startP;
@property (weak, nonatomic) UIImageView *imageV;
@property (nonatomic, weak) UIView *clipView; //设置裁剪显示的View

- (UIView *)clipView{
    if (_clipView == nil) {
        UIView *view = [[UIView alloc] init];
        _clipView = view;

        view.backgroundColor = [UIColor blackColor];
        view.alpha = 0.5;

        [self.view addSubview:view];
    }

    return _clipView;
}


UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];

[self.view addGestureRecognizer:pan];


//裁剪
- (void)pan:(UIPanGestureRecognizer *)pan
{
    CGPoint endA = CGPointZero;
    if (pan.state == UIGestureRecognizerStateBegan) { // 一开始拖动的时候

        // 获取一开始触摸点
      _startP = [pan locationInView:self.view];

    }else if(pan.state == UIGestureRecognizerStateChanged){ // 一直拖动
        // 获取结束点
         endA = [pan locationInView:self.view];

        CGFloat w = endA.x - _startP.x;
        CGFloat h = endA.y - _startP.y;

        // 获取截取范围
        CGRect clipRect = CGRectMake(_startP.x, _startP.y, w, h);

        // 生成截屏的view
        self.clipView.frame = clipRect;
    }
    else if (pan.state == UIGestureRecognizerStateEnded){

        // 图片裁剪,生成一张新的图片

        // 开启上下文
        // 如果不透明,默认超出裁剪区域会变成黑色,通常都是透明
        UIGraphicsBeginImageContextWithOptions(_imageV.bounds.size, NO, 0);

        // 设置裁剪区域
       UIBezierPath *path =  [UIBezierPath bezierPathWithRect:_clipView.frame];

        [path addClip];
        // 获取上下文
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        // 把控件上的内容渲染到上下文
        [_imageV.layer renderInContext:ctx];

        // 生成一张新的图片
        _imageV.image = UIGraphicsGetImageFromCurrentImageContext();

        // 关闭上下文
        UIGraphicsEndImageContext();

        // 先移除
        [_clipView removeFromSuperview];
        // 截取的view设置为nil
        _clipView = nil;   
    }
}

4.屏幕截屏

//1.开启位图上下文
 UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0);
//2.获取上下文
 CGContextRef ctx = UIGraphicsGetCurrentContext();
//3.把控件上的图层渲染到上下文,layer只能渲染
[view.layer renderInContext:ctx];
//4.生成一张图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//5.关闭上下文
UIGraphicsEndImageContext();

5.图片擦除

//在拖动手势中做图片擦除
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];    
[self.view addGestureRecognizer:pan];


- (void)pan:(UIPanGestureRecognizer *)pan
{
    //1.获取当前点
    CGPoint curP = [pan locationInView:self.view];
    //2.获取擦除的矩形范围
    CGFloat wh = 100;
    CGFloat x = curP.x - wh * 0.5;
    CGFloat y = curP.y - wh * 0.5;
    CGRect rect = CGRectMake(x, y, wh, wh);
    //3.开启上下文
    UIGraphicsBeginImageContextWithOptions(self.view     .bounds.size, NO, 0);
    //4.获取上下文
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //5.把控件的layer渲染上去
    [_imageView.layer renderInContext:ctx];
    //6.擦除图片
    CGContextClearRect(ctx, rect);
    //7.生成一张图片
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    _imageView.image = image;
    //8.关闭上下文
    UIGraphicsEndImageContext();

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值