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
3.2.2查看问题
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调用的漏写。
例如:
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,如何修改权限