CoreGraphics框架

1.介绍

Quartz2D是二维绘图引擎,支持iOS,macOS
在iOS中,体现为CoreGraphics框架

2.作用

绘制图形
绘制文字
绘制/生成图片
读取/生成pdf
截图/裁剪图片
自定义UI控件(手势解锁,统计图)
CGContextRef图形上下文的作用,保存绘制信息,决定输出目标(PDF,Bitmap,显示器窗口)

Quartz2D提供了几种类型的Context
Bitmap Graphics Context
PDF Graphics Context
Window Graphics Context
Layer Graphics Context
Printer Graphics Context

3.iOS中通过继承UIView重写drawRect可以直接获取到Context

-(void)drawRect:(CGRect)rect
{
//rect->bounds,rect指的是该view的bounds
}

4.实现基本图形,文本以及UIImage的绘制

@interface ZSView()

@property(nonatomic,strong) UIImageView * bgImg;

@end

@implementation ZSView

-(instancetype)initWithFrame:(CGRect)frame
{
    if(self = [super initWithFrame:frame])
    {
        self.bgImg=[[UIImageView alloc]initWithFrame:CGRectZero];
        
        [self addSubview:self.bgImg];
    }
    return self;;
}


//UI绘制的优化
-(void)draw
{
    //异步计算UI控件的颜色文字图片大小尺寸数据,然后在主线程上全部渲染到图片上
    CGRect rect = self.bounds;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        CGPoint point = CGPointMake(150, 150);
        UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0);
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        CGContextSetRGBFillColor(ctx, 0.5, 0.5, 0.5, 1);
        CGContextFillRect(ctx, CGRectMake(0, 0, 200, 200));
        NSMutableDictionary * dict =[NSMutableDictionary dictionary];
        [dict setObject:[UIFont systemFontOfSize:15] forKey:NSFontAttributeName];
        [dict setObject:[UIColor redColor] forKey:NSForegroundColorAttributeName];
        [@"内容区域" drawInRect:CGRectMake(0, 0, 200, 200) withAttributes:dict];
        
        [[UIImage imageNamed:@"release_driver"] drawAtPoint:point];
        UIImage *temp = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        //CGContextRelease(ctx);
        
        dispatch_async(dispatch_get_main_queue(), ^{
           
            self.bgImg.frame=rect;
            self.bgImg.image=temp;
        });

    });
    
}

//对于绘制出的图片进行点击区域设置
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    CGPoint location = [[touches anyObject] locationInView:self];
    
    CGRect frame = CGRectMake(0, 0, 200, 200);
    
    if (CGRectContainsPoint(frame, location)) {
        
        NSLog(@"点击");
    }
    
}

-(void)drawRect:(CGRect)rect
{
    
    /*绘制图形*/
    //1.绘制直线
    //[self drawLine];
    
    //2.绘制曲线
    //[self drawCurve];
    
    //3,绘制圆形
    //[self drawCircle];
    
    //4.绘制不规则路径
    //[self drawPath];
    
    /*绘制控件*/
    //1.绘制文字
    //[self drawText];
    
    //2.绘制图片
    //[self drawImage];
    
    //3.绘制Rect空间
    //[self drawRect];
    
}

#pragma mark - 绘制Rect空间
-(void)drawRect
{
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    CGRect rect = CGRectMake(0, 0, 300, 100);
    
    CGContextSetRGBFillColor(ctx, 0, 1.0, 0, 1);//设置填充色
    
    CGContextFillRect(ctx, rect);
    
    CGContextRelease(ctx);
}

#pragma mark - 绘制图片
-(void)drawImage
{
    
    //第一种方式
    CGPoint point = CGPointMake(100, 100);
    [[UIImage imageNamed:@"release_driver"] drawAtPoint:point];
 
    //第二种方式
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    UIImage * img = [UIImage imageNamed:@"release_driver"];
    //CGContextTranslateCTM(ctx,0.0f,self.frame.size.height);
    //CGContextScaleCTM(ctx,1.0,-1.0);
    CGContextDrawImage(ctx,CGRectMake(50,50, img.size.width, img.size.height), [img CGImage]);
    
    CGContextRelease(ctx);
    
}

#pragma mark - 绘制文字
-(void)drawText
{
    NSDictionary * dict=@{NSFontAttributeName:[UIFont systemFontOfSize:18],NSForegroundColorAttributeName:[UIColor orangeColor],NSStrokeColorAttributeName:[UIColor blueColor],NSStrokeWidthAttributeName:@2};
    [@"绘制文字" drawInRect:CGRectMake(10, 10, 50, 50) withAttributes:dict];
/*也可以通过Lable实现
    UILabel * label=[[UILabel alloc]initWithFrame:CGRectMake(10, 10, 100, 50)];
    NSString * text=@"绘制文字";
    [self.testview addSubview:label];
    NSDictionary * dict=@{NSFontAttributeName:[UIFont   systemFontOfSize:18],NSForegroundColorAttributeName:[UIColor orangeColor],NSStrokeColorAttributeName:[UIColor blueColor],NSStrokeWidthAttributeName:@2};
    NSAttributedString * str=[[NSAttributedString alloc]initWithString:text attributes:dict];
    label.attributedText=str;
*/
}

#pragma mark - 绘制路径
-(void)drawPath
{
    //1.获取上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    //2.创建路径
    CGMutablePathRef path = CGPathCreateMutable();//创建路径
    CGPathMoveToPoint(path, nil, 100, 100);//移动到指定位置(设置路径起点)
    CGPathAddLineToPoint(path, nil, 200, 300);//绘制直线(从起始位置开始)
    CGPathAddLineToPoint(path, nil,50, 100);//绘制直线(从起始位置开始)
    CGContextAddPath(context, path);//把路径添加到上下文(画布)中
    
    //3.设置图形上下文状态属性
    CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1);//设置笔触颜色
    CGContextSetRGBFillColor(context, 0, 1.0, 0, 1);//设置填充色
    CGContextSetLineWidth(context, 5.0);//设置线条宽度
    //4.绘制路径
    CGContextDrawPath(context, kCGPathStroke);//最后一个参数是填充类型
    
    //5.释放路径和上下文资源
    CGPathRelease(path);
    CGContextRelease(context);
}

#pragma mark - 绘制曲线
-(void)drawCurve
{
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextMoveToPoint(context, 100, 100);
    CGContextAddCurveToPoint(context, 200, 0, 300, 100, 100, 100);
    
    CGContextSetRGBStrokeColor(context, 0.5, 0.4, 0.3, 1);
    CGContextSetRGBFillColor(context, 0.3, 0.4, 0.5, 1);
    CGContextSetLineWidth(context, 1.0);
    
    CGContextDrawPath(context, kCGPathStroke);

    CGContextRelease(context);
}

#pragma mark - 绘制圆形
-(void)drawCircle
{
    //1.获取上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
    //2.创建圆形路径
    CGContextAddArc(context, 100, 100, 50, 0, 2*M_PI, 0);
    //3.设置上下文属性
    CGContextSetRGBStrokeColor(context, 0.5, 0.4, 0.3, 1);//设置笔触颜色
    CGContextSetRGBFillColor(context, 0.3, 0.4, 0.5, 1);//设置填充色
    CGContextSetLineWidth(context, 1.0);//设置线条宽度
    //4.绘制路径
    CGContextDrawPath(context, kCGPathFill);
    //5.释放资源
    CGContextRelease(context);
}

#pragma mark - 绘制直线
-(void)drawLine
{
    //1.获取上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    //2.创建路径
    CGContextMoveToPoint(context, 100, 100);
    CGContextAddLineToPoint(context, 200, 200);
    
    //3.设置图形上下文状态属性
    CGContextSetRGBStrokeColor(context, 1.0, 0, 0, 1);//设置笔触颜色
    CGContextSetRGBFillColor(context, 0, 1.0, 0, 1);//设置填充色
    CGContextSetLineWidth(context, 5.0);//设置线条宽度
    CGContextSetLineCap(context, kCGLineCapButt);//设置顶点样式
    CGContextSetLineJoin(context, kCGLineJoinMiter);//设置连接点样式
    CGFloat lengths[5] = {18,9,6,3,1};
    CGContextSetLineDash(context, 0, lengths, 5);
    CGContextSetShadowWithColor(context, CGSizeMake(2, 2), 0, [UIColor blueColor].CGColor);
    
    //4.绘制路径
    CGContextDrawPath(context, kCGPathFillStroke);//最后一个参数是填充类型
    
    //5.释放上下文资源
    CGContextRelease(context);
}

#pragma mark - 绘制直线(第二种方式),使用UIBezierPath替代CGPathRef
-(void)drawLine2
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 3);
    [[UIColor blackColor]setStroke];
    
    //UIBezierPath是对CGPathRef的面向对象封装
    UIBezierPath * path=[UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(0, 0)];
    [path addLineToPoint:CGPointMake(50,50)];
    CGContextAddPath(context, path.CGPath);
   
    CGContextStrokePath(context);
}

#pragma mark - 绘制直线(第三种方式),直接使用UIBezierPath
-(void)drawLine3
{
    UIBezierPath * path=[UIBezierPath bezierPathWithRect:CGRectMake(10, 10, 30, 30)];
    //内部帮我们实现获取CGContextRef和设置Path的功能
    [path stroke];
}

4.Context上下文状态栈

Context的状态(颜色,笔宽等属性)可以保存在栈中,可以保存多个状态到状态栈,但是只能取栈顶状态到上下文中使用。
绘制两条不同颜色的线条

CGContextRef context = UIGraphicsGetCurrentContext();
    //设置上下文状态
    CGContextSetLineWidth(context, 3);
    [[UIColor blackColor]setStroke];
    //把该上下文状态存储到栈中(栈中1元素,这次状态位于栈顶)
    CGContextSaveGState(context);
    
    //再次设置上下文状态(覆盖当前上下文状态)
    CGContextSetLineWidth(context, 3);
    [[UIColor redColor]setStroke];
    //把该上下文状态存储到栈中(栈中2元素,这次状态位于栈顶)
    CGContextSaveGState(context);
    
    //使用当前上下文状态描绘路径(Red)
    UIBezierPath * path=[UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(0, 0)];
    [path addLineToPoint:CGPointMake(50,50)];
    CGContextAddPath(context, path.CGPath);
    CGContextStrokePath(context);
    
    
    UIBezierPath * path2=[UIBezierPath bezierPath];
    [path2 moveToPoint:CGPointMake(80, 80)];
    [path2 addLineToPoint:CGPointMake(80,20)];
    CGContextAddPath(context, path2.CGPath);
    //把(Black)状态从栈取出到栈顶并设置上下文状态
    CGContextRestoreGState(context);
    CGContextRestoreGState(context);
    CGContextStrokePath(context);

5.应用

a.合成图片(给图片加水印)

    //使用BitmapContext上下文
    UIImage * bgimage=[UIImage imageNamed:@"bgimg"];
    UIImage * logo=[UIImage imageNamed:@"logo"];
    //开始上下文
    UIGraphicsBeginImageContext(bgimage.size);
    //把两张图片渲染到上下文中
    [bgimage drawAtPoint:CGPointZero];
    [logo drawAtPoint:CGPointMake(50, 50)];
    //合成图像
    UIImage * newImage=UIGraphicsGetImageFromCurrentImageContext();
    //结束上下文绘制
    UIGraphicsEndImageContext();
    UIImageView * imgV=[[UIImageView alloc]initWithFrame:self.bounds];
    [self addSubview:imgV];
    imgV.image=newImage;
    
    /*更好的方式是进行异步绘制
    UIImageView * imgV=[[UIImageView alloc]initWithFrame:self.bounds];
    [self addSubview:imgV];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        UIImage * bgimage=[UIImage imageNamed:@"bgimg"];
        UIImage * logo=[UIImage imageNamed:@"logo"];
        UIGraphicsBeginImageContext(bgimage.size);
        [bgimage drawAtPoint:CGPointZero];
        [logo drawAtPoint:CGPointMake(50, 50)];
        UIImage * newImage=UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        dispatch_async(dispatch_get_main_queue(), ^{
            imgV.image=newImage;
        });
    });
    */

b.裁剪圆形图片

UIImage * bgimage=[UIImage imageNamed:@"bgimg"];
    UIGraphicsBeginImageContext(bgimage.size);
    //设置裁剪区域
    UIBezierPath * path=[UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, bgimage.size.width, bgimage.size.height)];
    [path addClip];
   //把图片渲染到上下文中
    [bgimage drawAtPoint:CGPointZero];
    UIImage * newImage=UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    UIImageView * imgV=[[UIImageView alloc]initWithFrame:self.bounds];
    [self addSubview:imgV];
    imgV.image=newImage;

c.裁剪带边框的圆形图片

UIImage * bgimage=[UIImage imageNamed:@"bgimg"];
    CGSize contextSize=CGSizeMake(bgimage.size.width+20, bgimage.size.height+20);
    //设置上下文大小比图片宽度大10
    UIGraphicsBeginImageContext(contextSize);
    //填充一个橙色的圆形
    UIBezierPath * path=[UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, contextSize.width, contextSize.height)];
    [[UIColor orangeColor]set];
    [path fill];
    //设置裁剪区域
    UIBezierPath * path2=[UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, bgimage.size.width, bgimage.size.height)];
    [path2 addClip];
    //把图片渲染到上下文中
    [bgimage drawAtPoint:CGPointMake(10, 10)];
    UIImage * newImage=UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    UIImageView * imgV=[[UIImageView alloc]initWithFrame:self.bounds];
    [self addSubview:imgV];
    imgV.image=newImage;

d.截屏

    /*
      第二个参数表示图像上下文是不是要透明-YES不透明。
      [UIScreen mainScreen].scale是像素和点的一个桥接,在iOS-UIKit中使用点作为单位,其实内部就是像素*scale的结果
      [UIScreen mainScreen].scale-iphone8-2
      [UIScreen mainScreen].scale-iphone8p-3
      值得注意的是CoreGraphics框架使用的是像素作为单位
    */
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, YES, [UIScreen mainScreen].scale);
    //UIGraphicsBeginImageContext(self.bounds.size);
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //把Layer被渲染到CGContext上
    [self.layer renderInContext:ctx];
    UIImage * newImage=UIGraphicsGetImageFromCurrentImageContext();
    NSData * data=UIImagePNGRepresentation(newImage);
    [data writeToFile:@"/Users/zhousiyang/Downloads/1.png" atomically:YES];
    UIGraphicsEndImageContext();

e.拖拽截图

@interface TestViewController ()
{
    UIView * maskView;
    CGPoint startP;
}
@property (weak, nonatomic) IBOutlet UIImageView *bgImgV;

@end

@implementation TestViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIPanGestureRecognizer * pan=[[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
    [self.bgImgV addGestureRecognizer:pan];
}

-(void)pan:(UIPanGestureRecognizer*)pan
{
    
    if(pan.state==UIGestureRecognizerStateBegan)
    {
        startP=[pan locationInView:self.bgImgV];
    }
    if(pan.state==UIGestureRecognizerStateChanged)
    {
        CGPoint panP=[pan translationInView:self.bgImgV];
        CGFloat x=startP.x;
        CGFloat y=startP.y;
        CGFloat w=panP.x;
        CGFloat h=panP.y;
        if(maskView==nil)
        {
            maskView=[[UIView alloc]initWithFrame:CGRectMake(x, y, w, h)];
            maskView.backgroundColor=[UIColor colorWithWhite:0.5 alpha:0.5];
            [self.bgImgV addSubview:maskView];
        }else
        {
            maskView.frame=CGRectMake(x, y, w, h);
        }
    }
    if(pan.state==UIGestureRecognizerStateEnded)
    {
        [self screencapture];
        [maskView removeFromSuperview];
        maskView=nil;
        
    }
}


-(void)screencapture
{
    //1.开启图片上下文(大小就是被截图View大小)
    UIGraphicsBeginImageContextWithOptions(self.bgImgV.bounds.size, YES, 0.0);
    NSLog(@"1%@",NSStringFromCGSize(self.bgImgV.bounds.size));
    //2.设置Clip区域
    UIBezierPath * path=[UIBezierPath bezierPathWithRect:CGRectMake(maskView.frame.origin.x, maskView.frame.origin.y, maskView.frame.size.width, maskView.frame.size.height)];
    [path addClip];
    //或者UIRectClip(maskView.frame);
    CGContextRef ctx=UIGraphicsGetCurrentContext();
    //3.把图片渲染到上下文中
    [self.bgImgV.layer renderInContext:ctx];
    //4.获取上下文中的图片(大小图片上下文大小-被截图View大小)
    UIImage * newImage=UIGraphicsGetImageFromCurrentImageContext();
    NSLog(@"2%@",NSStringFromCGSize(newImage.size));
    UIGraphicsEndImageContext();
    self.bgImgV.image=newImage;
}

f.手势解锁
思路:
布局一个有9个按钮固定大小View的解锁界面,界面外边不响应滑动事件;(事件响应
滑动到按钮范围内,按钮变为选中状态,把滑到过的按钮添加到数组中;(touches...
把数组中的按钮按照中心点连接绘制起来,末端加一个最后绘制的点;(drawRect,UIBezierPath
绘制完毕保存按钮的tag;(touchesEnd

@interface LockView()
{
    BOOL isEnd;
    CGPoint curP;
    NSMutableArray * btnArr;
}
@end

@implementation LockView

-(void)awakeFromNib
{
    [super awakeFromNib];
    
    btnArr=[NSMutableArray array];
    
    for (int i=0; i<9; i++) {
        UIButton * btn=[[UIButton alloc]init];
        [btn setUserInteractionEnabled:NO];
        btn.tag=i;
        btn.backgroundColor=[UIColor colorWithWhite:0.5 alpha:0.5];
        btn.titleLabel.font=[UIFont systemFontOfSize:13];
        [btn setTitle:[NSString stringWithFormat:@"%d未选中",i] forState:UIControlStateNormal];
        [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        [btn setTitle:[NSString stringWithFormat:@"%d选中",i] forState:UIControlStateSelected];
        [btn setTitleColor:[UIColor blueColor] forState:UIControlStateSelected];
        [self addSubview:btn];
    }
}

-(void)layoutSubviews
{
    float border=(self.frame.size.width-150)/4;
    for (int i=0; i<9; i++) {
        float x=border+(border+50)*(i%3);
        float y=border+(border+50)*(i/3);
        float w=50;
        float h=50;
        UIButton * btn=self.subviews[i];
        btn.frame=CGRectMake(x, y, w, h);
    }
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //点击在按钮范围,按钮被选中
    UITouch * touch=[touches anyObject];
    CGPoint startP=[touch locationInView:self];
    for (UIButton * btn in self.subviews) {
        if(CGRectContainsPoint(btn.frame, startP))
        {
            [btnArr addObject:btn];
            btn.selected=YES;
            break;
        }
    }
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    //滑动在按钮范围,按钮被选中
    UITouch * touch=[touches anyObject];
    curP=[touch locationInView:self];
    for (UIButton * btn in self.subviews) {
        if(CGRectContainsPoint(btn.frame,curP)&&btn.selected==NO)
        {
            [btnArr addObject:btn];
            btn.selected=YES;
            break;
        }
    }
    //调用drawRect绘制线条
    [self setNeedsDisplay];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    isEnd=YES;
    [self setNeedsDisplay];
}

-(void)drawRect:(CGRect)rect
{
    if(btnArr.count>0)
    {
        CGContextRef ctx=UIGraphicsGetCurrentContext();
        [[UIColor redColor]set];
        CGContextSetLineJoin(ctx, kCGLineJoinRound);
        CGContextSetLineWidth(ctx, 10);
        UIBezierPath * path=[UIBezierPath bezierPath];
        for (int i=0; i

g.画板
思路:
手滑过的路径可以通过手势或者touch...方法监听。
绘制:每条路径通过UIBezierPath,所有路径保存在数组中,进行drawRect重绘
颜色和线宽:通过UIBezierPath属性修改,颜色通过上下文修改
撤销:删除数组末位元素,重绘
清屏:删除数组全部元素,重绘
橡皮擦:使用画板底色画笔绘制
优化:通过 [self.drawview setNeedsDisplay]以及drawRect重绘(CPU),如果绘制很多很多线条会导致内存和CPU暴增,思路是把绘制的Path变成一张图片,在手势移动的时候绘制上去。

    CAShapeLayer * shaperLayer=[[CAShapeLayer alloc]init];
    shaperLayer.path=path.CGPath;
    [self.view.layer addSublayer:shaperLayer];

h.抽奖转盘
关键点:
布局UI,键盘上每个小格子如果使用UIButton,要设置anchorPoint=(0.5,1),position=背景View.center。
旋转动画的交互,使用UIView动画,不能使用CA核心动画。
从大切图截取部分图片的问题,使用CoreGrapyics框架问题。

    UIImage * img=[UIImage imageNamed:@""];
    float w=img.size.width;
    float h=img.size.height;
    //CoreGraphyics框架是像素点为单位,截取时必须乘以[UIScreen mainScreen].scale
    CGImageCreateWithImageInRect(img.CGImage, CGRectMake(0, 0, w*[UIScreen mainScreen].scale, h*[UIScreen mainScreen].scale));

你可能感兴趣的:(CoreGraphics框架)