Quartz2D-2

1.矩阵操作

1.1.平移

- (void)drawRect:(CGRect)rect {
    //1.获得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.矩阵操作
    //X轴偏移量,Y轴偏移量
    CGContextTranslateCTM(ctx, 100, 100);
    //3.绘制图形,并添加到上下文
    //绘制圆
    CGContextAddArc(ctx, rect.size.width*0.5, rect.size.height*0.5, 100, 0, M_PI*2, 1);
    //绘制直线
    CGContextMoveToPoint(ctx, 0, 0);
    CGContextAddLineToPoint(ctx, rect.size.width, rect.size.height);
    //设置属性
    CGContextSetLineWidth(ctx, 10);
    //4.渲染
    CGContextStrokePath(ctx);
}

1.2.旋转

#define angle2Arc(angle) (angle * M_PI / 180)
- (void)drawRect:(CGRect)rect {
    //1.获得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.矩阵操作
    //旋转弧度
    CGContextRotateCTM(ctx, angle2Arc(10));
    //3.绘制图形,并添加到上下文
    //绘制圆
    CGContextAddArc(ctx, rect.size.width*0.5, rect.size.height*0.5, 100, 0, M_PI*2, 1);
    //绘制直线
    CGContextMoveToPoint(ctx, 0, 0);
    CGContextAddLineToPoint(ctx, rect.size.width, rect.size.height);
    //设置属性
    CGContextSetLineWidth(ctx, 10);
    //4.渲染
    CGContextStrokePath(ctx);
}

1.3.缩放

- (void)drawRect:(CGRect)rect {
    //1.获得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.矩阵操作
    //X轴缩放比例,Y轴缩放比例
    CGContextScaleCTM(ctx, 0.5, 0.5);
    //3.绘制图形,并添加到上下文
    //绘制圆
    CGContextAddArc(ctx, rect.size.width*0.5, rect.size.height*0.5, 100, 0, M_PI*2, 1);
    //绘制直线
    CGContextMoveToPoint(ctx, 0, 0);
    CGContextAddLineToPoint(ctx, rect.size.width, rect.size.height);
    //设置属性
    CGContextSetLineWidth(ctx, 10);
    //4.渲染
    CGContextStrokePath(ctx);
}

1.4.注意

1.一定要先做矩阵操作,然后再绘制图形.(也就是矩阵操作在添加路径之前有效)
2.只会对矩阵操作之后并且在渲染之前绘制的图形有效.

2.图形上下文栈

2.1.通过绘图原理来理解图形上下文栈

当我们把上下文中的图形渲染到UIView上,其实就是把上下文中的图形搬到了UIView中,
上下文中没有了图形,但是上下文还在,上下文中的图形状态(颜色,线宽,样式)还在.
那我们还是可以继续画线的.

那图形上下文栈呢用来干什么呢?
其实就是我们想在操作图形上下文中之前,先把原始的图形上下文备份一下.
然后再操作,再渲染后,再把图形上下文恢复,恢复之后继续绘图就可以了.

2.2.栈的特性

先进后出

2.3.图形上下文栈操作

//入栈
void CGContextSaveGState(CGContextRef c)
  1.图形上下文栈中保存的仅仅是当前的绘图状态信息(颜色,线宽,样式,矩阵).
  2.跟上下文中的路径没有关系

//出栈
CGContextRestoreGState(ctx);
  1.将栈顶的绘图状态取出,替换掉当前的图形上下文中的绘图状态.
    (也就是说出栈操作就是在设置绘图状态而已)
  2.恢复的位置在渲染之前都是可以的.

2.4.解析代码

- (void)drawRect:(CGRect)rect {
    //1.获得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //备份-入栈
    CGContextSaveGState(ctx);
    //3.绘制图形,并添加到上下文
    //绘制直线
    CGContextMoveToPoint(ctx, 0, 0);
    CGContextAddLineToPoint(ctx, rect.size.width, rect.size.height);
    //设置属性
    CGContextSetLineWidth(ctx, 10);
    [[UIColor redColor] set];
    //4.渲染
    CGContextStrokePath(ctx);
    //绘制圆
    CGContextAddArc(ctx, rect.size.width*0.5, rect.size.height*0.5, 100, 0, M_PI*2, 1);
    //恢复-出栈
    CGContextRestoreGState(ctx);
    //渲染
    CGContextStrokePath(ctx);
}
解析:
1.获得上下文就是准备了草稿纸
2.入栈:把当前上下文的状态拷贝一份保存到栈中
3.添加路径:往草稿纸中绘制路径
4.设置属性:修改当前上下文的状态信息
5.渲染:把上下文中的路径在设置状态后搬到UIView中显示.
6.再往上下文中绘制圆
7.出栈:把当前上下文中的状态替换成出栈的属性,并且删除栈顶提出的状态.
8.渲染:继续把上下文中的路径在设置状态后搬到UIView中显示.

3.Quartz2D内存管理

3.1.CGMutablePathRef路径方式问题

这段代码是存在内存泄露的问题。

- (void)drawRect:(CGRect)rect {
    //1.获得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.绘制路径
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, 100, 100);
    CGPathAddLineToPoint(path, NULL, 200, 200);
    //3将路径添加到上下文中
    CGContextAddPath(ctx, path);
    //4.渲染
    CGContextStrokePath(ctx);
}

3.2.C语言内存分析

3.2.1分析

Xcode --> Product --> Analyze


Quartz2D-2_第1张图片

3.2.2查看问题

Quartz2D-2_第2张图片

Quartz2D-2_第3张图片

3.3.内存泄露解决方式

在代码中如果有用到C语言的方法获得一个对象。例如使用方法中带有 create,retain,copy这些词的方法获得的对象,我们一定要在不使用这个对象后进行release操作。

解决上边问题的方式

- (void)drawRect:(CGRect)rect {
    //1.获得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.绘制路径
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, 100, 100);
    CGPathAddLineToPoint(path, NULL, 200, 200);
    //3将路径添加到上下文中
    CGContextAddPath(ctx, path);
    //4.渲染
    CGContextStrokePath(ctx);
    //5.释放内存
    CGPathRelease(path);//第一种方式
    // CFRelease(path);//第二种方式
}

解析:
1.CGXxxCreate 对应就有 CGXxxRelease
2.通过CFRelease(任何类型),可以释放任何类型.

3.4.Analyze其它功能

可以分析出项目中类的生命周期的super调用的漏写。
例如:


Quartz2D-2_第4张图片

4.绘制文字

4.1绘制文字

// 从某个点开始绘制,并且只绘制一行,超出区域不显示
- (void)drawRect:(CGRect)rect {
    //1.创建文字
    NSString *str = @"传智播客";
    //2.绘制文字
    //point:从哪个点开始绘制
    //attribute:文字属性,可以传nil(就是默认的属性)
    [str drawAtPoint:CGPointZero withAttributes:nil];
}
//在某个区域内绘制,会换行,超出区域不显示
- (void)drawRect:(CGRect)rect {
    //1.创建文字
    NSString *str = @"传智播客";
    //2.绘制文字
    //rect:在哪个区域内绘制
    //attribute:文字属性,可以传nil(就是默认的属性)
    [str drawInRect:CGRectMake(100, 100, 40, 60) withAttributes:nil];
}

4.2文字属性

文字属性在 UIKit 框架的 NSAttributedString.h 中

NSFontAttributeName  字体
NSParagraphStyleAttributeName  设置段落样式
NSForegroundColorAttributeName  字体颜色
NSBackgroundColorAttributeName  背景色
NSLigatureAttributeName 
NSKernAttributeName  调整字句 kerning 字句调整
NSStrikethroughStyleAttributeName  删除线
NSUnderlineStyleAttributeName  下划线
NSStrokeColorAttributeName  设置文字描边颜色,需要和NSStrokeWidthAttributeName设置描边宽度,这样就能使文字空心.
NSStrokeWidthAttributeName  设置描边宽度
NSShadowAttributeName 设置阴影,单独设置不好使,必须和其他属性搭配才好使。和这三个任一个都好使,NSVerticalGlyphFormAttributeName,NSObliquenessAttributeName,NSExpansionAttributeName
NSTextEffectAttributeName
NSAttachmentAttributeName
NSLinkAttributeName
NSBaselineOffsetAttributeName
NSUnderlineColorAttributeName  下划线颜色
NSStrikethroughColorAttributeName   删除线颜色
NSObliquenessAttributeName  设置字体倾斜
NSExpansionAttributeName  设置文本扁平化
NSWritingDirectionAttributeName
NSVerticalGlyphFormAttributeName 该属性所对应的值是一个 NSNumber 对象(整数)。0 表示横排文本。1 表示竖排文本。在 iOS 中,总是使用横排文本,0 以外的值都未定义。

5.绘制图片

5.1绘制图片-OC方式

//从某个点开始绘制-图片会按照原比例绘制
-(void)drawImage{
    //1.创建图片
    UIImage *image = [UIImage imageNamed:@"hero"];
    //2.绘制图片
    [image drawAtPoint:CGPointZero];
}
//在某个区域内进行绘制-图片会被拉伸或者压缩
-(void)drawImage:(CGRect)rect{
    //1.创建图片
    UIImage *image = [UIImage imageNamed:@"hero"];
    //2.绘制图片
    [image drawInRect:rect];
}
//会平铺在某个区域内.
-(void)drawImage:(CGRect)rect{
    //1.创建图片
    UIImage *image = [UIImage imageNamed:@"hero"];
    //2.绘制图片
    [image drawAsPatternInRect:rect];
}
-(void)drawImage:(CGRect)rect{
    //1.创建图片
    UIImage *image = [UIImage imageNamed:@"hero"];
    //注意: blendMode 是绘制图片的参数
    //2.绘制图片--从某个点开始绘制--原比例绘制
    [image drawAtPoint:CGPointZero blendMode:kCGBlendModeNormal alpha:0.3];
    //2.绘制图片--在某块区域内进行绘制--图片会被拉伸或者压缩
    // [image drawInRect:rect blendMode:kCGBlendModeNormal alpha:0.3];
}

5.2图片属性

 kCGBlendModeNormal,  正常;也是默认的模式。前景图会覆盖背景图
 kCGBlendModeMultiply,  正片叠底;混合了前景和背景的颜色,最终颜色比原先的都暗
 kCGBlendModeScreen,   滤色;把前景和背景图的颜色先反过来,然后混合
 kCGBlendModeOverlay,  覆盖;能保留灰度信息,结合kCGBlendModeSaturation能保留透明度信息,在imageWithBlendMode方法中两次执行drawInRect方法实现我们基本需求
 kCGBlendModeDarken,  变暗
 kCGBlendModeLighten,  变亮
 kCGBlendModeColorDodge, 颜色变淡
 kCGBlendModeColorBurn,  颜色加深
 kCGBlendModeSoftLight,  柔光
 kCGBlendModeHardLight,  强光
 kCGBlendModeDifference,  插值
 kCGBlendModeExclusion,  排除
 kCGBlendModeHue,  色调
 kCGBlendModeSaturation, 饱和度
 kCGBlendModeColor,  颜色
 kCGBlendModeLuminosity,  亮度
 
 //Apple额外定义的枚举
 //R: premultiplied result, 表示混合结果
 //S: Source, 表示源颜色(Sa对应透明度值: 0.0-1.0)
 //D: destination colors with alpha, 表示带透明度的目标颜色(Da对应透明度值: 0.0-1.0)
 
kCGBlendModeClear,  R = 0
kCGBlendModeCopy,  R = S
kCGBlendModeSourceIn,  R = S*Da
kCGBlendModeSourceOut,  R = S*(1 - Da)
kCGBlendModeSourceAtop,  R = S*Da + D*(1 - Sa)
kCGBlendModeDestinationOver,  R = S*(1 - Da) + D
kCGBlendModeDestinationIn,  R = D*Sa;能保留透明度信息
kCGBlendModeDestinationOut,  R = D*(1 - Sa)
kCGBlendModeDestinationAtop,  R = S*(1 - Da) + D*Sa
kCGBlendModeXOR,  R = S*(1 - Da) + D*(1 - Sa)
kCGBlendModePlusDarker,  R = MAX(0, (1 - D) + (1 - S))
kCGBlendModePlusLighter  R = MIN(1, S + D)(最后一种混合模式)

5.3绘制图片--C方式

注意:C的方式绘图会按照Quartz2D的坐标系来绘制.所以显示的图片都是反向的。

-(void)drawImageWithC:(CGRect)rect{
    //1.获得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.加载图片
    UIImage *image = [UIImage imageNamed:@"dog"];
    //3.绘制图片
    // 在指定的区域绘制图片
    // 以填充方式绘制图片-图片会被拉伸或者压缩
    CGContextDrawImage(ctx, rect, image.CGImage);
}
-(void)drawImageWithC:(CGRect)rect{
    //1.获得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //2.加载图片
    UIImage *image = [UIImage imageNamed:@"dog"];
    //3.绘制图片
    // 以平铺方式绘图
    //注意:这个rect 是设置图片的大小,我们一般给原比例就可以了
    CGContextDrawTiledImage(ctx, CGRectMake(0, 0, 100, 100), image.CGImage);
}

6.模拟ImageView

6.1.系统UIImageView的init 和 initWithImage的区别

init 方法创建的对象默认没有大小
initWithImage 方法创建的对象默认有大小

6.2.模拟ImageView

1.自定义一个UIView,起名CZImageView

2.头文件中声明image属性

@property(nonatomic,strong)UIImage *image;

3.将传入的图片绘制到设备上

- (void)drawRect:(CGRect)rect{
    //将图片绘制到屏幕上
    [self.image drawInRect:rect];
}

4.重写image的set方法,当传入图片时重绘界面

- (void)setImage:(UIImage *)image{
    _image = image;
    //重绘界面
    [self setNeedsDisplay];
}

5.自定义 initWithImage 方法

- (instancetype)initWithImage:(UIImage *)image{
    self = [super initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
    if (self) {
        self.image = image;
    }
    return self;
}

7.裁剪图片

7.1.裁剪上下文显示区域

- (void)drawRect:(CGRect)rect{
    //1.获得图片对象
    UIImage *image = [UIImage imageNamed:@"me"];
    //2.获得上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //3.绘制上下文显示区域
    CGContextAddArc(ctx, rect.size.width*0.5, rect.size.height*0.5, 150, 0, M_PI*2, 1);
    //4.裁剪
    CGContextClip(ctx);
    //5.绘制图片
    [image drawInRect:rect];
}

7.2注意:

裁剪的是上下文的显示区域,不是说裁剪的上下文,上下文该是多大就还是多大,就是哪一块需要显示改变了.

8.图片类型上下文

8.1基本使用

1.需求
绘制一个UIimage图片

2.使用方式

- (void)imageContext{
    //1.开启一个图片类型的上下文
    UIGraphicsBeginImageContext(CGSizeMake(300, 300));
    /*
     开启一个图片类型的上下文
     size : 上下文大小 -- 像素
     opaque : 不透明(上下文这张草稿纸是否透明) YES表示不透明 NO表示透明
     scale : 缩放 会缩放上下文 (重要参数) -- 可以根据屏幕像素比例生成想要的图片   0表示当前设置(方法内部做了判断)
     */
    // UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 300), YES, 1);
    //2.获得当前的图片类型上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    //3.绘制图形
    CGContextMoveToPoint(ctx, 0, 0);
    CGContextAddLineToPoint(ctx, 300, 300);
    
    //4.渲染
    CGContextStrokePath(ctx);
    
    //5.获得图片对象
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    //6.关闭上下文
    UIGraphicsEndImageContext();
    
    //使用我们绘制图片
    self.imageView.image = newImage;
}

8.2把图片存入沙盒

- (void)writeToFile:(UIImage *)image{
    
    //1.创建路径
    NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    filePath = [filePath stringByAppendingPathComponent:@"xx.png"];

    //2.把image保存为png图片
    NSData *pngdata = UIImagePNGRepresentation(image);
    
    /*
     把image保存为jpeg图片
     compressionQuality是质量,jpeg图片是可以压缩的
     */
    // NSData *jpegdata = UIImageJPEGRepresentation(image, 1);
    
    //3.通过NSData写入沙盒
    [pngdata writeToFile:filePath atomically:YES];

}

8.2裁剪图片

- (UIImage *)imageContext:(UIImage *)image{
    
    //1.开启一个图片类型的上下文
    UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
    
    //2.获得当前的图片类型上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    //3.绘制裁剪图形
    CGContextAddArc(ctx, image.size.width*0.5, image.size.height*0.5, image.size.width*0.5, 0, M_PI*2, 1);
    //4.裁剪上下文
    CGContextClip(ctx);
    
    //5.渲染图片
    [image drawAtPoint:CGPointZero];
    
    //6.获得图片对象
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    //7.关闭上下文
    UIGraphicsEndImageContext();
    
    //8.返回裁剪后的图片
    return newImage;
}

8把图片保到相册

8.1使用

- (void)imageWriteToFile:(UIImage *)image{
    /*
     image : 要保存的图片
     completionTarget : 监听对象
     completionSelector : 监听回调方法
     contextInfo : 上下文
     */
    UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}

/*
 保存到相册的回调
 image : 保存的图片
 error : 保存信息
 contextInfo : 上下文 - 上边传什么这里就返回什么
 */
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{
    if(error){
        NSLog(@"保存失败");
    }else{
        NSLog(@"保存成功");
    }
}

8.2访问相册权限问题

1弹出

2如果选择了NO,如何修改权限

8.3.报错问题

你可能感兴趣的:(Quartz2D-2)