美文网首页
QuartzCore(绘图)

QuartzCore(绘图)

作者: Xl_Lee | 来源:发表于2021-04-01 00:57 被阅读0次

    什么是Quartz2D

    Quartz2D 是一个二维绘图引擎

    1. 绘制图形:线条、三角形、矩形、圆形、弧等
    2. 绘制文字
    3. 绘制生成图片(图像)
    4. 读取、生成PDF
    5. 截取、裁剪图片
    6. 自定义UI控件
    7. 裁剪图片
    8. 涂鸦、画板
    9. 手势解锁
    10. 报表:折线图、饼状图、柱状图(github 搜 iOS chat)

    Quartz2D在iOS开发中的价值

    有些UI界面及其复杂、而且比较个性化,用普通的UI控件无法实现,这时可以利用Quartz2D技术奖控件内部的结构画出来,自定义控件的样子

    其实,iOS中大部分控件的内容都是通过Quartz2D画出来的
    因此,Quartz2D在iOS开发中最重要的一个价值是:自定义UI控件

    • 图形上下文
    • 图形上下文( Graphics Context):是一个 CGContextref类型的数据
    • 图形上下文的作用
    1. 保存绘图信息、绘图状态
    2. 决定绘制的输出目标(绘制到什么地方去?)
      (输出目标可以是PDF文件、 Bitmap或者显示器的窗口上)

    绘制好的图片 (保存)-> 图形上下文 (显示)-> 输出目标

    • 相同的一套绘图序列,指定不同的 Graphics Context,就可将相同的图像绘制到不同的目标 上

    • Quartz2D提供了以下几种类型的 Graphics Context

    1. Bitmap Graphics
    2. Context PDF Graphics Context
    3. Window Graphics Context
    4. Layer Graphics Context

    自定义控件

    • 如何利用 Quartz.2D自定义view?(自定义UI控件)
    • 如何利用 Quart2D绘制东西到view上?
    1. 首先,得有图形上下文,因为它能保存绘图信息,并且决定着绘制到什么地方去
    2. 其次,那个图形上下文必须跟view相关联,オ能将内容绘制到view上面

    自定义view的步骤

    1. 新建一个类,继承自 Uiview
    2. 实现-(void)drawRect:( CGRect)rect方法(实现这个方法会出现内存问题,例如画板里面,出现内存问题),(已经默认实现了图形上下文直接获取)然后在这个方法中取得跟当前view相关联的图形上下文
    3. 把上下文渲染到layer层

    上下文状态栈

    
      CGContextSaveGState(ctx)
         1. 把当前上下文的状态保存到上下文状态栈,相当于copy一份状态,状态是设置的
    属性比如颜色,宽度等。 
        
      CGContextRestoreGState(ctx)
        1. 从当前的上下文状态栈中取出栈顶的状态,覆盖掉当前的上下文状态(取出状
                态栈顶部的状态属性,覆盖当前上下文的状态)
       
      CGContextStrokePath(ctx)
        1. 取出上下文当中所有绘制的路径
        2. 把上下文当中的状态应用到所有路径当中,
    
    

    UIBeziePath

    @implementation XLShopBeziePath
    
    #pragma mark - life cycle
    
    - (instancetype)init {
        if (self = [super init]) {
    //        + (instancetype)bezierPath;
            // 画矩形
    //        + (instancetype)bezierPathWithRect:(CGRect)rect;
            // 画椭圆
    //        + (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
            // 画矩形带有圆角
    //        + (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;
            
    //        + (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
            // 画弧
            // center:弧所在的圆心
            // radius: 弧所在的半径
            // startAngle:弧开始的角度,0度数,在圆的最右侧,向上度数是负数,向下度数是正的
            // endAngle:弧结束的角度。到哪个位置
            // clockwise: 是否为顺时针,怎么样到这个位置
    //        + (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
    //        + (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
        }
        return self;
    }
    
    @end
    
    #import "DrawView.h"
    
    @implementation DrawView
    
    #pragma mark - life cycle
    
    //作用:专门用来绘图
    //什么时候调用:当View显示时调用
    //参数:当前View的 bounds
    - (void)drawRect:(CGRect)rect {
        [super drawRect:rect];
       // [self drawCurveLine];
       // 其内部实现了1,2,3,4 步骤
      UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
        [[UIColor redColor] set];
        [path fill];
    }
    
    #pragma mark - private methods
    
    - (void)drawLine {
        //在此方法内部会自动创建一个跟Vie相关联的上下文
        //可以直接获取
        //无论是开启上下文,还是获取上下文,都是以UIGraphics 
        //1,获取当前跟View相关联的上下文
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        
        //2.描述路径
        // 一个路径可以描述多条线
        UIBezierPath *path = [UIBezierPath bezierPath];
         
        //2.1设置起点
        //坐标原点是以当前绘制View的左上角为(0,0) 
        [path moveToPoint:CGPointMake(50, 50)];
    
        //2.2添加一根线到某个点,画了一条线
        [path addLineToPoint:CGPointMake(150, 150)];
        // 在当前画板新开一个起点再次画一条线
        [path moveToPoint:CGPointMake(100, 50)];
        [path addLineToPoint:CGPointMake(150, 250)];
        
        // 把上一个路径的重点,作为下一个路径的起点
        [path addLineToPoint:CGPointMake(250, 50)];
        
        
        //设置上下文的状态 
        //设置线宽度
     
        CGContextSetLineWidth(ctx, 10);
        //设置上下文的连接样式
        CGContextSetLineJoin(ctx, kCGLineJoinRound);
        //设置顶角样式
        CGContextSetLineCap(ctx, kCGLineCapRound);
        
        // 设置线条颜色
        // 方式一
        CGContextSetRGBStrokeColor(ctx, 1, 1, 0, 1);
        // 方式二
        //设置线的颜色
        //setStroke setFill
        //如果直接使用set,会自动匹配渲染的模式
        //[[UIColor redColor]  set];
    
        
        
         //把路径添加到上下文
        // 3,把路径添加到上下文
        CGContextAddPath(ctx, path.CGPath);
        //4.把上下文的当中绘制的所有路径渲染View相关联的ayer当中, 
        //渲染的方式有两种:
        //描边: stroke 
        //填充:fi11
        CGContextStrokePath(ctx);
    }
    - (void)drawCurveLine {
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        UIBezierPath *path = [UIBezierPath bezierPath];
        // 1. 画曲线
        [path moveToPoint:CGPointMake(50, 150)];  
         //2. 添加一根曲线到某个点,添加一个控制点
        [path addQuadCurveToPoint:CGPointMake(250, 150) controlPoint:CGPointMake(150, 50)];
         //3. 把路径添加到上下文
        CGContextAddPath(ctx, path.CGPath);
         //4. 把上下文的内容渲染到View的layer;
        CGContextStrokePath(ctx);
    }
    
    @end
    
    

    扇形进度条

    @implementation ProgressView
    
    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super initWithFrame:frame]) {
            self.backgroundColor = [UIColor darkGrayColor];        
        }
        return self;
    }
    
    - (void)setPregress:(CGFloat)pregress {
        _pregress = pregress;
        // 通知系统重新绘制调用drawRect
        [self setNeedsDisplay];
    }
    - (void)drawRect:(CGRect)rect {
        [super drawRect:rect];
        // 原点
        CGPoint center = CGPointMake(rect.size.width * 0.5, rect.size.height * 0.5);
        // 半径
        CGFloat radius = rect.size.width * 0.5 - 10;
        // 开始弧度
        CGFloat start = -M_PI_2;
        // 结束弧度,开始的弧度加上360弧度回到开始的位置
        CGFloat end = start + M_PI * self.pregress * 2;
        // 获取上下文
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        // 曲线
        UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:start endAngle:end clockwise:YES];
        
        [bezierPath addLineToPoint:center];
        // 添加到上下文
        CGContextAddPath(ctx, bezierPath.CGPath);
        
        [[UIColor colorWithRed:1 green:0 blue:1 alpha:0.8] set];
        //绘制路径
        // 线条路径
    //    CGContextStrokePath(ctx);
        // 填充路径
        CGContextFillPath(ctx);
    }
    
    - (UIColor *)randColor  {
        CGFloat r = arc4random_uniform(256);
        CGFloat g = arc4random_uniform(256);
        CGFloat b = arc4random_uniform(256);
        return [UIColor colorWithRed:r green:g blue:b alpha:1];
    }
    @end
    

    CADisplayLink的使用

    #import "XLShopDrawAnimationImage.h"
    static CGFloat showY = 0;
    @interface XLShopDrawAnimationImage ()
    @property (nonatomic, strong) CADisplayLink *displayLink;
    @end
    @implementation XLShopDrawAnimationImage
    
    #pragma mark - life cycle
    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super initWithFrame:frame]) {
        }
        return self;
    }
    
    - (void)drawRect:(CGRect)rect {
        [super drawRect:rect];
        UIImage *image = [UIImage imageNamed:@"雪花"];
        [image drawAtPoint:CGPointMake(0, showY)];
    }
    
    #pragma mark - public methos
    - (void)startDisPlayLink {
        self.displayLink.paused = NO;
    }
    - (void)pausedDisplayLink {
        self.displayLink.paused = YES;
    }
    - (void)invalidateDisplayLink {
        [self.displayLink invalidate];
        self.displayLink = nil;
    }
    #pragma mark - private methods
    
    - (void)update {
        showY += 50;
        if (showY > self.frame.size.height) {
            showY = 0;
        }
        [self setNeedsDisplay];
    
    }
    
    #pragma mark - setters and getters
    
    - (CADisplayLink *)displayLink {
        if (_displayLink == nil) {
            //为什么会很流畅?
            
            //当每一次屏幕刷新新时调用,(屏幕每一秒刷新60次)
            //  setNeedsDisplay会调用 drawRect
            //  井不是立马调用,是当屏幕刷新时才去调用 drawRect ,它跟CADisplayLink执行方法时间是相同的
            CADisplayLink *displayLink = [CADisplayLink  displayLinkWithTarget:self selector:@selector(update)];
            _displayLink = displayLink;   
            [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:( NSRunLoopCommonModes)];
        }
        return _displayLink;
    }
    @end
    
    

    画文字

    @implementation XLShopDrawText
    #pragma mark - life cycle
    - (void)drawRect:(CGRect)rect {
        [super drawRect:rect];
        NSString *text = @"drawText";
        
        NSDictionary *atribute = 
        @{
            // 设置字体
            NSFontAttributeName:[UIFont boldSystemFontOfSize:20],
            // 设置颜色
            NSForegroundColorAttributeName:[UIColor redColor],
            // 设置描边
            NSStrokeColorAttributeName: [UIColor blueColor],
            NSStrokeWidthAttributeName: @3,
            // 设置阴影
            NSShadowAttributeName: ({
                NSShadow *shadow = [[NSShadow alloc] init];
                shadow.shadowColor = [UIColor greenColor];
                // 设置阴影的偏移量
                shadow.shadowOffset = CGSizeMake(10, 10);
                shadow;
            })
        };
        [text drawAtPoint:CGPointZero withAttributes:atribute];
        
    }
    
    @end
    

    图片加水印

    1. 位图上下文
    要手动去创違一个位图上下文 创建位图上下文时,要指定大小
    指定大小,决定着生成图片的尺寸是多大
    
    2. 从上下文当中生成一张图片
    把上下文当中绘制的所有内容合成一起生成张跟上下文尺寸 大小一样的图片
    
    3. 手动创建的上下文,一定要手动去销毁掉
    
    @implementation UIImage (Txt)
    
    - (UIImage *)imageName:(NSString *)name txt:(NSString *)txt {
        if (name == nil || name.length == 0) {
            return nil;
        }
        UIImage *image = [UIImage imageNamed:name];
    //    1. 位图上下文
    //    要手动去创違一个位图上下文 创建位图上下文时,要指定大小
    //    指定大小,决定着生成图片的尺寸是多大
        UIGraphicsBeginImageContext(image.size);
    //    2. 从上下文当中生成一张图片
    //    把上下文当中绘制的所有内容合成一起生成张跟上下文尺寸 大小一样的图片
        [image  drawAtPoint:CGPointZero];
        [txt drawAtPoint:CGPointMake(image.size.width / 2.f, image.size.height / 2) withAttributes:@{}];
        image = UIGraphicsGetImageFromCurrentImageContext();
    //    3. 手动创建的上下文,一定要手动去销毁掉
        UIGraphicsEndImageContext();
        return image;
    }
    
    @end
    

    裁剪图片

    C语言的API,rect是point,需要转换成像素
    手动转换成像素 = 图片size * [UIScreen mainScreen].scale

    裁剪图片的区域
    
    CGImageCreateWithImageInRect(<#CGImageRef  _Nullable image#>, <#CGRect rect#>)
    

    OC语言裁剪size内部会转成像素size

    - (UIImage *)imageClicpedWithImageName:(NSString *)name {
        if (name == nil || name.length == 0) {
            return nil;
        }
        //1,加图片
        UIImage* image = [UIImage imageNamed:name];
        //2.开启一个位图上下文 
        UIGraphicsBeginImageContext(image.size);
        //3.设置一个圆形的裁剪区域
        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
    
        //把路径设置为裁剪区域(超出裁剪区域以外的内容会被自动剪掉)
        [path addClip];//对后面绘制的内容有效果,已经绘制到上下文当中的内容,不会被裁剪 
        //4,把图片绘制到上下文当中
        [image drawAtPoint:CGPointZero];
        //5,从上下文当中生成一张图片
        image  = UIGraphicsGetImageFromCurrentImageContext();
        //6,关闭上下文
        UIGraphicsEndImageContext();
        return image;
    }
    
    

    带有边框的裁剪

    边框宽度
    1.先开启一个图片上下文 尺寸大小在原始图片基础上宽高都加上两倍边框宽度
    2.填充一个圆形路径.这个圆形路径大小,和上下文尺寸大小一样.
    3.添加一个小圆,小圆,xy从边框宽度位置开始添加,宽高和原始图片一样大小.把小圆设为裁剪区域
    4.把图片给绘制上去
    
    + (UIImage *)imageClicpedWithImageName:(NSString *)name {
     
        return [self imageClicpedWithImageName:name borderWidth:0];
    }
    
    + (UIImage *)imageClicpedWithImageName:(NSString *)name borderWidth:(CGFloat)borderWith {
        if (name == nil || name.length == 0) {
            return nil;
        }
        //1,加图片
        UIImage* image = [UIImage imageNamed:name];
        //2.开启一个位图上下文 
        CGSize size = CGSizeMake(image.size.width + borderWith * 2, image.size.width + borderWith * 2);
        UIGraphicsBeginImageContext(size);
        //3 尺寸大小在原始图片基础上宽高都加上两倍边框宽度
        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, size.width, size.width)];
        [[UIColor purpleColor] set];
        [path fill];
        //4 添加一个小圆,小圆,xy从边框宽度位置开始添加,宽高和原始图片一样大小.把小圆设为裁剪区域
        UIBezierPath *imagePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(borderWith, borderWith, size.width - 2 * borderWith, size.width - 2 * borderWith)];
        [imagePath addClip];
        //5.把图片给绘制上去
        [image drawAtPoint:CGPointMake(borderWith, borderWith)];
        //6. 关闭位图上下文
        image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image;
        
    }
    
    +(UIImage *)imageWithScreenCaptureView:(UIView *)view {
        if (view == nil || ![view isKindOfClass:[UIView class]]) {
            return nil;
        }
        // 生成一张图片
        // 1. 开启位图上下文
        UIGraphicsBeginImageContext(view.bounds.size);
        // 2. 获取当前的上下文
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        // 3. 把控制器view的内容绘制到上下文当中,(上下文于layer之间是通过渲染方式进行交互)
        [view.layer renderInContext:ctx];
        // 4. 从上下文当中生成一张图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        
        // 5. 关闭上下文
        UIGraphicsEndImageContext();
        
        return image;
        
    }
    
    
    - (UIImage *)imageWithScreenCaptureView:(UIView *)view scale:(BOOL)scale isOpaque:(BOOL)opaque{
        //2.0当前加載的图片是@2X
        //3.0当前加载的图片是3X
        //当前像素坐标与点坐标的比例
        //在0C像素坐标会自动根据比例转成点坐标
        //在C语言当中是不会转换
        //[UIScreen mainScreen].scale
        
        if (view == nil || ![view isKindOfClass:[UIView class]]) {
            return nil;
        }
        // 生成一张图片
        // 1. 开启位图上下文
        // 参数: 
        //  opaque(不透明)
        //  scale == 0 (系统内部自动 * [UIScreen mainScreen].scale),生成图片的大小 = viewSize * scale。
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, opaque, scale);
        // 2. 获取当前的上下文
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        // 3. 把控制器view的内容绘制到上下文当中,(上下文于layer之间是通过渲染方式进行交互)
        [view.layer renderInContext:ctx];
        // 4. 从上下文当中生成一张图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
            // 5. 关闭上下文
        UIGraphicsEndImageContext();
        return image;
    }
    

    图片遮罩裁剪

    @interface XLShopCoverImageView ()
    @property (nonatomic, strong) UIImageView *imageView;
    @property (nonatomic, strong) UIView *coverView;
    @property (nonatomic, assign) CGFloat beginX;
    @property (nonatomic, assign) CGFloat beginY;
    @end
    @implementation XLShopCoverImageView
    
    #pragma mark - life cycle
    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super initWithFrame:frame]) {
            self.clipsToBounds = YES;
            [self addSubview:self.imageView];
        }
        return self;
    }
    - (void)layoutSubviews {
        [super layoutSubviews];
        self.imageView.frame = self.bounds;
    }
    
    #pragma mark event response
    
    - (void)panGes:(UIPanGestureRecognizer *)panGes {
        CGPoint point  = [panGes locationInView:self.imageView];
        if (panGes.state == UIGestureRecognizerStateBegan) {
            self.beginX = point.x;
            self.beginY = point.y;
        } else if (panGes.state == UIGestureRecognizerStateChanged) {
            CGFloat coverX = self.beginX;
            CGFloat coverY = self.beginY;
            CGFloat coverViewWidth = point.x - self.beginX;
            CGFloat coverViewHeight = point.y - self.beginY;
            self.coverView.frame = CGRectMake(coverX, coverY, coverViewWidth, coverViewHeight);
        } else if (panGes.state == UIGestureRecognizerStateEnded) {
            // 1. 开启位图上下文
            UIGraphicsBeginImageContextWithOptions(self.imageView.frame.size, NO, 0);
            // 2. 设置裁剪区域
            UIRectClip(self.coverView.frame);
            // 3. 将图片渲染到上下文中
            // 3.1 获取刚才创建的上下文
            CGContextRef ctx = UIGraphicsGetCurrentContext();
            // 3.2 通过layer把imageView内容层渲染给上下文
            [self.imageView.layer renderInContext:ctx];
            // 4. 获取裁剪后的图片
            UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
            // 5. 把上下文关闭
            UIGraphicsEndImageContext();
            [self.coverView removeFromSuperview];
            
            // 把刚才裁剪的图片显示在imageView上
            self.imageView.image = image;
        }
    }
    
    #pragma mark - public methods
    - (void)setImageName:(NSString *)imageName {
        _imageName = imageName;
        self.imageView.image = [UIImage imageNamed:imageName];
    }
    
    #pragma mark - getter and setters
    
    - (UIImageView *)imageView {
        if (_imageView == nil) {
            _imageView = [[UIImageView alloc] init];
            _imageView.userInteractionEnabled = YES;
            UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGes:)];
            [_imageView addGestureRecognizer:panGes];
        }
        return _imageView;
    }
    - (UIView *)coverView {
        if (_coverView == nil) {
            UIView *view = [UIView new];
            view.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
            [self addSubview:view];
            _coverView = view;
        }
        return _coverView;
    }
    
    @end
    

    图片擦除

    - (void)panGes:(UIPanGestureRecognizer *)panGes {
        CGPoint point = [panGes locationInView:self.imageView];
        CGFloat with = 30, height = 30;
        CGFloat x = point.x - 10, y = point.y - 10;
        UIGraphicsBeginImageContextWithOptions(self.imageView.bounds.size, YES, 0.0);
        CGContextRef ctr = UIGraphicsGetCurrentContext();
        [self.imageView.layer renderInContext:ctr];
        
        CGContextClearRect(ctr, CGRectMake(x, y, with, height));
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        self.imageView.image = image;
        UIGraphicsEndImageContext();
    }
    

    判断一个点是否在某个按钮的身上

    - (UIButton *)btnRectContainsPoint:(CGPoint)point {
        for (UIButton *btn in self.subviews) {
            if (CGRectContainsPoint(btn.frame, point)) {
                return btn;
            }
        }
        return nil;
    }
    

    自定义UIBezierPath

    @interface XLShopBezierPath : UIBezierPath
    @property (nonatomic, strong) UIColor *lineColor;
    @end
    @implementation XLShopBezierPath
    #pragma mark - life cycle
    
    - (instancetype)init {
        if (self = [super init]) {
    //        + (instancetype)bezierPath;
            // 画矩形
    //        + (instancetype)bezierPathWithRect:(CGRect)rect;
            // 画椭圆
    //        + (instancetype)bezierPathWithOvalInRect:(CGRect)rect;
            // 画矩形带有圆角
    //        + (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;
            
    //        + (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;
            // 画弧
            // center:弧所在的圆心
            // radius: 弧所在的半径
            // startAngle:弧开始的角度,0度数,在圆的最右侧,向上度数是负数,向下度数是正的
            // endAngle:弧结束的角度。到哪个位置
            // clockwise: 是否为顺时针,怎么样到这个位置
    //        + (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
    //        + (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;
        }
        return self;
    }
    @end
    

    自定义可旋转捏合缩放拖动的XLShopHandleImageView

    .h

    @class XLShopHandleImageView;
    @protocol XLShopHandleImageViewDelegate <NSObject>
    
    -(void)xlShopHandleImageView:(nullable XLShopHandleImageView *)view newImage:(nullable UIImage *)image;
    
    @end
    NS_ASSUME_NONNULL_BEGIN
    
    @interface XLShopHandleImageView : UIView
    @property (nonatomic, weak) id<XLShopHandleImageViewDelegate>deletate;
    @property (nonatomic, strong) UIImage *image;
    @end
    NS_ASSUME_NONNULL_END
    
    

    .m

    @interface XLShopHandleImageView ()<UIGestureRecognizerDelegate>
    @property (nonatomic, weak) UIImageView *imageView;
    @end
    @implementation XLShopHandleImageView
    
    #pragma mark - life cycle
    
    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super initWithFrame:frame]) {
            self.backgroundColor = [UIColor clearColor];
            self.clipsToBounds = YES;
        }
        return self;
    }
    
    
    #pragma mark - event response
    
    - (void)rotationGes:(UIRotationGestureRecognizer *)rorationGes {
        // 获取旋转角度(已经是弧度)
        // 相对于最原始的弧度
        CGFloat roration =  rorationGes.rotation;
        rorationGes.view.transform = CGAffineTransformRotate(rorationGes.view.transform, roration);
        // 清零操作,每次旋转都是以弧度为0的基础上进行旋转
        [rorationGes setRotation:0];
    }
    
    - (void)pinGes:(UIPinchGestureRecognizer *)pin {
        // 放大缩小
        // 获取缩放比例相对于最原始的比例
        CGFloat scale = pin.scale;
        pin.view.transform = CGAffineTransformScale(pin.view.transform, scale, scale);
        // 置1操作,每一次操作都是在sacle= 1基础下进行缩放
        [pin setScale:1];
        
    }
    
    // 获取的偏移量是相对于最原始的点
    - (void)panGes:(UIPanGestureRecognizer *)pan {
        CGPoint point = [pan translationInView:pan.view];
        pan.view.transform = CGAffineTransformTranslate(pan.view.transform, point.x, point.y);
        // 清零操作,每次都是以偏移量为0的基础上进行偏移
        [pan setTranslation:CGPointMake(0, 0) inView:pan.view];
    }
    
    - (void)longGes:(UILongPressGestureRecognizer *)longGes {
        if (longGes.state == UIGestureRecognizerStateBegan) {
            // 实现闪亮效果
            [UIView animateWithDuration:0.5 animations:^{
                self.imageView.alpha = 0; 
            } completion:^(BOOL finished) {
                self.imageView.alpha = 1;
    
                // 生成图片
                // 1.开启位图
                UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
                // 2.获取上下文
                CGContextRef ctr =  UIGraphicsGetCurrentContext();
                // 3.把self.layer层渲染到上下文,如果把imageView的layer层渲染到上下文,会导致imageView的形变没有改变,是通过原点(正方向)所在的坐标系渲染到上下文中
                [self.layer renderInContext:ctr];
                // 4.获取图片
                UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
                // 5.关闭上下文
                UIGraphicsEndImageContext();
                // 6. 通知代理生成图片
                if (self.deletate && [self.deletate respondsToSelector:@selector(xlShopHandleImageView:newImage:)]) {
                    [self.deletate xlShopHandleImageView:self newImage:image];
                }
                // 移除操作
                [self removeFromSuperview];
            }];
        }
    }
    #pragma mark - UIGestureRecognizerDelegate
    // 代理实现协议方法,通知手势可以让Target支持多个手势
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
        return YES;
    }
    
    
    #pragma mark - getters and setters
    
    - (UIImageView *)imageView {
        if (_imageView == nil) {
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds];
            imageView.userInteractionEnabled = YES;
            [self addSubview:imageView];
            // 手势默认是不能同时支持多个手势的,可以指定代理,让代理实现shouldRecognizeSimultaneouslyWithGestureRecognizer 返回YES,返回多个手势
           
            UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGes:)];
            [imageView addGestureRecognizer:panGes];
            // 相对于坐标系,坐标系有大小有方向
            //        - (CGPoint)translationInView:(nullable UIView *)view;
            // 转换后清零操作
            //        - (void)setTranslation:(CGPoint)translation inView:(nullable UIView *)view
            
    
            // 捏合(放大缩小)
            UIPinchGestureRecognizer *pinGes = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinGes:)];
            pinGes.delegate = self;
            [imageView addGestureRecognizer:pinGes];
            
            // 旋转
            UIRotationGestureRecognizer *rotationGes = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotationGes:)];
            rotationGes.delegate = self;
            [imageView addGestureRecognizer:rotationGes];
            
            // 长按
            UILongPressGestureRecognizer *longGes = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longGes:)];
            [imageView addGestureRecognizer:longGes];
            _imageView = imageView;
            
            
        }
        return _imageView;
    }
    #pragma mark - getters and setters
    
    - (void)setImage:(UIImage *)image {
        _image = image;
        self.imageView.image = image;
    }
    @end
    
    

    画板

    .h

    #import <UIKit/UIKit.h>
    @class XLShopBezierPath,XLShopHandleImageView;
    
    NS_ASSUME_NONNULL_BEGIN
    @interface XLShopDrawingBoardView : UIView
    @property (nonatomic, strong,readwrite,nullable) UIImage *backgroundImage;
    @property (nonatomic, strong,readwrite,nullable) UIColor *lineColor;
    @property (nonatomic, readwrite) CGFloat lineWidth;
    - (void)setCanHandleFrontImage:(UIImage *)canHandleFrontImage;
    - (void)imageWithScreenCaptureViewCallback:(void(^)(UIImage* image))callback;
    - (void)imageSaveToPhotosAlbumCallback:(void(^)(UIImage *image, NSError *error))saveToAlbumCallback;
    - (void)clear;
    - (void)back;
    - (void)front;
    - (void)erase;
    
    @end
    

    .m

    #import "XLShopDrawingBoardView.h"
    #import "XLShopBezierPath.h"
    #import "XLShopHandleImageView.h"
    
    @interface XLShopDrawingBoardView ()<XLShopHandleImageViewDelegate>
    @property (nonatomic, weak) XLShopHandleImageView *handleImageView;
    @property (nonatomic, copy) void(^saveToAlbumCallback)(UIImage *image, NSError *error);
    @property (nonatomic, strong) NSMutableArray *pathArray;
    @property (nonatomic, strong) NSMutableArray *pathPopedArray;
    @property (nonatomic, strong) UIView *contentView;
    @property (nonatomic, strong) XLShopBezierPath *bezierPath;
    @end
    @implementation XLShopDrawingBoardView
    
    #pragma mark - life cycle
    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super initWithFrame:frame]) {
            self.backgroundColor = [UIColor whiteColor];
            [self addSubview:self.contentView];
            self.lineColor = [UIColor blackColor];
            self.lineWidth = 12;
        }
        return self;
    }
    - (void)drawRect:(CGRect)rect {
        for (XLShopBezierPath *path in self.pathArray) {
            // 颜色必须在drawRect方法绘制
            if ([path isKindOfClass:[UIImage class]]) {
                UIImage *image = (UIImage *)path;
                [image drawInRect:rect];
            } else {
                [path.lineColor set];
                [path stroke];
            }
        }
    }
    - (void)layoutSubviews {
        [super layoutSubviews];
        self.contentView.frame = self.bounds;
    }
    
    #pragma mark - event response
    - (void)panGes:(UIPanGestureRecognizer *)panGes {
        CGPoint point = [panGes locationInView:self.contentView];
        if (!CGRectContainsPoint(panGes.view.frame, point)) {
            return;
        }
        if (panGes.state == UIGestureRecognizerStateBegan) {
            self.bezierPath = [XLShopBezierPath bezierPath];
            self.bezierPath.lineWidth = self.lineWidth;
            self.bezierPath.lineColor = self.lineColor;
            self.bezierPath.lineJoinStyle = kCGLineJoinRound;
            self.bezierPath.lineCapStyle = kCGLineCapRound;
            [self.pathArray addObject:self.bezierPath];
            [self.bezierPath moveToPoint:point];
        } else if (panGes.state == UIGestureRecognizerStateChanged) {
            [self.bezierPath addLineToPoint:point];
            [self setNeedsDisplay];
        }
    }
    
    
    #pragma mark - XLShopHandleImageViewDelegate
    -(void)xlShopHandleImageView:(nullable XLShopHandleImageView *)view newImage:(nullable UIImage *)image {
        self.backgroundImage = image;
    }
    
    #pragma mark - public methods
    
    - (void)clear {
        [self.pathArray removeAllObjects];
        [self.pathPopedArray removeAllObjects];
        [self setNeedsDisplay];
    }
    - (void)back {
        UIBezierPath *lastBezierPath =  self.pathArray.lastObject;
        if(lastBezierPath == nil) return;
        [self.pathPopedArray addObject:lastBezierPath];
        [self.pathArray removeObject:lastBezierPath];
        [self setNeedsDisplay];
    }
    - (void)front {
        UIBezierPath *lastBezierPath =  self.pathPopedArray.lastObject;
        if(lastBezierPath == nil) return;
        [self.pathArray addObject:lastBezierPath];
        [self.pathPopedArray removeObject:lastBezierPath];
        [self setNeedsDisplay];
    }
    - (void)erase {
        self.lineColor = [UIColor whiteColor];
    }
    
    - (void)imageWithScreenCaptureViewCallback:(void(^)(UIImage* image))callback {
        if(!callback) return;
        UIImage *image = [self screenCapturView];
        callback(image);
    }
    
    - (void)imageSaveToPhotosAlbumCallback:(void(^)(UIImage *image, NSError *error))saveToAlbumCallback {
        self.saveToAlbumCallback = saveToAlbumCallback;
        UIImage *image = [self screenCapturView];
        UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
    }
    
    #pragma mark - private methods 
    
    - (UIImage *)screenCapturView {
        UIGraphicsBeginImageContextWithOptions(self.contentView.frame.size, NO, 0);
        CGContextRef ctr = UIGraphicsGetCurrentContext();
        [self.layer renderInContext:ctr];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image;
    }
    
    -(void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
        if (!self.saveToAlbumCallback) return;
        self.saveToAlbumCallback(image, error);
    }
    
    #pragma mark - getters and setters
    
    - (NSMutableArray *)pathArray {
        if (_pathArray == nil) {
            _pathArray = [NSMutableArray array];
        }
        return _pathArray;
    }
    
    - (NSMutableArray *)pathPopedArray {
        if (_pathPopedArray == nil) {
            _pathPopedArray = [NSMutableArray array];
        }
        return _pathPopedArray;
    }
    
    - (UIView *)contentView {
        if (_contentView == nil) {
            _contentView = [UIView new];
            _contentView.backgroundColor = [UIColor clearColor];
            UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGes:)];
            [_contentView addGestureRecognizer:panGes];
        }
        return _contentView;;
    }
    
    - (XLShopHandleImageView *)handleImageView {
        if (_handleImageView == nil) {
            XLShopHandleImageView *handleImageView = [[XLShopHandleImageView alloc] initWithFrame:self.contentView.bounds];
            handleImageView.deletate = self;
            [self.contentView addSubview:handleImageView];
            _handleImageView = handleImageView;
        }
        return _handleImageView;
    }
    - (void)setBackgroundImage:(UIImage *)backgroundImage {
        if (!backgroundImage || ![backgroundImage isKindOfClass:[UIImage class]]) return;
        _backgroundImage = backgroundImage;
        [self.pathArray addObject:backgroundImage];
        [self setNeedsDisplay];
    }
    - (void)setCanHandleFrontImage:(UIImage *)canHandleFrontImage {
        self.handleImageView.image = canHandleFrontImage;
    }
    @end
    
    

    相关文章

      网友评论

          本文标题:QuartzCore(绘图)

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