IOS开发之UI进阶(Quartz2D图形上下文)

Quartz2D

Quartz2D是一个二维绘图引擎,同时支持IOSMac OS X系统(跨平台,纯C语言),包括在CoreGraphics框架中

  • 绘制图形、文字,生成图片
  • 读取/生成PDF
  • 截图/裁剪图片
  • 自定义UI控件

数据类型以CG作为前缀

Quartz2D的类型

  • 图形上下文CGContextRef
  • Quartz2D提供了以下几种类型的Graphics Context:
  • Bitmap Graphics Context
  • PDF Graphics Context
  • Window Graphics Context
  • Layer Graphics Context (UI控件)
  • Printer Graphics Context (打印机)
  • 主要包括这些信息:绘图路径(各种各样的图形)、绘图状态(颜色、线宽、样式、旋转、缩放、平移、图片裁剪区域等)、输出目标(输出到哪里)

UIKit框架

  • 对部分Quartz2DAPI做封装
  • 没有封装的只能调用原生的
  • 比如:画图片、文字到控件上(UIKit已经封装好了)

绘图的步骤

  • 绘制到UIView上面,新建一个继承自UIView的类
在其drawRect方法中绘制,方法1 C
- (void)drawRect:(CGRect)rect {
    // Drawing code
    // 1.获取当前绘图上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 2.把路径添加到上下文当中
    CGContextMoveToPoint(ctx,50,50); // 起点 移动笔
    CGContextAddLineToPoint(ctx,100,100); // 终点 从起点画到终点
	// 2.5可以设置线的样式 可以省略 
	CGContextSetLineWidth(ctx,10); // 线宽10
	CGContextSetLineJoin(ctx,kCGLineJoinRound); // 连接处圆弧,kCGLineJoinMiter默认,kCGLineJoinRound圆弧,kCGLineJoinBevel切角
	CGContextSetLineCap(ctx,kCGLineCapRound); // 头尾样式 kCGLineCapButt默认,kCGLineCapRound圆弧,kCGLineCapSquare加个平角(相比默认会长一点)
	CGContextSetRGBStrokeColor(ctx,0.4,0.4,0.4,1); // 设置颜色 ctx + RGB三个数值 + 透明度 
    // 3.渲染
    CGContextStrokePath(ctx);
}
方法2 C
- (void)drawRect:(CGRect)rect {
    // Drawing code
    // 1.获取当前绘图上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 2.拼接路径
    CGMutablePathRef path = CGPathCreateMutable();
    CGContextMoveToPoint(path,NULL,50,50);
    CGContextAddLineToPoint(path,NULL,100,100);
    // 3.把路径添加到上下文
  	CGContextStrokePath(ctx,path);
    // 4.渲染
    CGContextStrokePath(ctx);
}
方法3 C+OC
 - (void)drawRect:(CGRect)rect {
    // Drawing code
    // 1.获取当前绘图上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 2.拼接路径
    // OC封装
    UIBezierPath path = [[UIBezierPath alloc] init];
	[path moveToPoint:CGPointMake(50,50)];
	[path addLineToPoint:CGPointMake(100,100)];
    // 3.把路径添加到上下文
  	CGContextStrokePath(ctx,path.CGPath);
    // 4.渲染
    CGContextStrokePath(ctx);
}
  • CGMutablePathRef 转化为 UIBezierPath
	CGMutablePathRef path = CGPathCreateMutable();
	UIBezierPath *path1 = [UIBezierPath bezierPathWithCGPath:path];
方法4 OC **最常用的**
 - (void)drawRect:(CGRect)rect {
    // Drawing code
    // 创建路径对象
    UIBezierPath *path = [UIBezierPath bezierPath];
	// 通过路径对象,拼接路径
	[path moveToPoint:CGPointMake(50,50)];
	[path addLineToPoint:CGPointMake(100,100)];
	// 设置样式 可省略
	[path setLineWidth:10]; // 设置线宽
	[path setLineJoinStyle:kCGLineJoinRound]; // 设置连接处样式
	[path setLineCapStyle:kCGLineCapRound]; // 设置头尾的样式
	[[UIColor blueColor] setStroke]; // 设置颜色
	// 渲染
	[path stroke];
}

理解(void)drawRect:(CGRect)rect

  • CGContextRef是在drawRect函数内部获取的,而不是创建的
  • (CGRect)rectviewbounds
  • drawRect是系统调用的,view第一次显示或者重绘的时候会调用
  • 重绘:调用某个需要重绘的view对象的setNeedsDisplay或者setNeedsDisplayInRect(Rect是重绘的区域)
[self setNeedsDisplay];
  • 手动调用drawRect的时候可能获取不到正确的上下文

绘制图形

  • 矩形
 - (void)drawRect:(CGRect)rect {
    // Drawing code
    // 创建路径对象
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(100,100,100,100)];
	// 渲染
	[path stroke];
}
  • 圆角矩形
    cornerRadius为正方形边的一半时,变成圆形
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(100,100,100,100) cornerRadius:10];
  • 椭圆
    长宽相等时,变成圆形
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,200,100)];

  • 在这里插入图片描述
该方法用于画弧线
// ArcCenter圆心 radius半径 startAngle起点 endAngle终点 clockwise顺时针
// 起点是三点钟方向是0  M _PI是π  二分之π是M_PI_2
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150,150) radius:100 startAngle:0 endAngle:2 * M_PI clockwise:1];

渲染方式(描边,填充)

C语言
CGContextClosePath(ctx); // 关闭路径,终点自动连接起点
CGContextStrokePath(ctx); // 描边,正常渲染使用的
CGContextFillPath(ctx); // 填充
CGContextDrawPath(ctx,kCGPathFill); // 可以调用多种方法,描边,填充,也可以同时描边填充,查看枚举选项就可以了
OC语言
[path closePath]; // 关闭路径
[path stroke]; // 描边渲染
[path fill]; // 填充,
// 同时描边填充,同时调用就可以了,区别于C的方法

even-odd rule:奇偶填充规则

一个特殊的渲染方式
C语言
CGContextDrawPath(ctx,kCGPathEOFill);
OC语言
path.usersEvenOddFillRule = YES;

非零绕数规则

一个默认的渲染方式,如果出现双圆重叠,就会出现

重绘

[self setNeedsDisplay]; // 整体重绘
[self setNeedsDisplayInRect:CGRectMake(0,0,150,150)]; // 重绘指定区域

矩阵操作(旋转、缩放、平移)

在添加上下文之前才能生效,而不是放在渲染之前

// 缩放
void CGContextScaleCTM(CGContextRef c,CGFloat sx,CGFloat sy);
// 旋转
void CGContextRotateCTM(CGContextRef c,CGFloat angle);
// 平移
void CGContextTranslateCTM(CGContextRef c,CGFloat tx,CGFloat ty);

保存状态和恢复状态

图形上下文栈,保存状态进栈,恢复则移出

// 保存 保存颜色和宽度
CGContextSaveGState(ctx);
// 恢复
CGContextRestoreGState(ctx);

Quartz2D内存管理

使用Path对象时的内存管理问题

  • 凡是在函数名字当中包含了retaincopycreate关键字,那么在调用完毕该函数,创建出的对象,在使用完毕以后,都需要进行release
  • 但是 CGPathCreateMutable()不是OC方法,所以不是调用某个对象的 release方法
  • Cgxxxxxcreate对应的就有 Cgxxxxxrelease
  • 通过 CFRelease(任何类型}可以释放任何类型
// 释放 两种方式
CGContextStrokePath(path); 
CFRelease(path);

绘制文字

- (void)drawRect:(CGRect)rect {
	NSString *str = @"测试";
	// 绘制
	// 方法一 attribute是字典可以传入文字多个属性 例如:@{NSFontAttributeName:[UIFont systemFontOfSize:30]}
	[str drawAtPoint:CGPonitZero withAttributes:nil];
	// 方法二
	[str drawInRect:CGRectMake(0,0,200,200) withAttributes:nil];
}

绘制图片

- (void)drawRect:(CGRect)rect {
	UIImage *image = [UIImage imageNamed:@"me"];
	// 绘制
	// 方法一 从点开始绘制
	[image drawAtPoint:CGPonitZero];
	// 方法二 绘制到某个区域 会拉伸
	[image drawInRect:CGRectMake(0,0,200,200)];
	// 方法三 小图平铺
	[image drawAsPatternInRect:rect];
}

裁剪

- (void)drawRect:(CGRect)rect {
	UIImage *image = [UIImage imageNamed:@"me"];
	// 获取上下文
	CGContextRef ctx = UIGraphicsGetCurrentContext();
	// 画显示的区域
	CGContextAddArc(ctx,150,150,150,0,2 * M_PI,1);
	// 裁剪 保留里面的内容 裁剪的是显示区域
	CGContextClip(ctx);
	// 拉伸显示到view
	[image drawInRect:rect];
}

绘制上下文到UIImageView

  • 不用在drawRect函数里也可以使用
  • 还可以转为NSData保存在沙盒里面
// 开启图片类型上下文
UIGraphicsBeginImageContext(CGSizeMake(300,300));//方法一
//UIGraphicsBeginImageContextWithOptions(CGSizeMake(300,300),NO,[UIScreen mainScreen].scale); 方法二:多了参数选择 透明 缩放倍速
//[UIScreen mainScreen].scale是屏幕的缩放因子,可以设置为0,自动获取  
 
// 获取当前的图片类型上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 拼接路径
CGContextMoveToPoint(ctx,50,50);
CGContextAddLineToPoint(ctx,100,100);
// 渲染
CGContextStrokePath(ctx);
// 从上下文获取图片对象
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭图片类型上下文
UIGraphicsEndImageContext();  
// 放到UIImageView上面
self.imageView.image = iamge;

// image保存到沙盒
// image转化为NSData
NSData *data = UIImagePNGRepresentation(image);
// filePath沙盒路径自动获取,png文件结尾,写入沙盒
[data writeToFile:filePath atomically:YES]; 

UIButton绘制圆角

左上:UIRectCornerTopLeft
左下:UIRectCornerBottomLeft
右上:UIRectCornerTopRight
右下:UIRectCornerBottomRight

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:button.bounds   byRoundingCorners:UIRectCornerBottomLeft |    UIRectCornerTopLeft    cornerRadii:CGSizeMake(8, 8)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = button.bounds;
maskLayer.path = maskPath.CGPath;
button.layer.mask = maskLayer;

UIImage添加文字水印和图片水印

+ (UIImage *)waterAtImage:(UIImage *)image
                   	 text:(NSString *)text
                    point:(CGPoint)point
               attributes:(NSDictionary *)attributes {
    // 开启图形上下文
    UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
    // 绘制图片
    [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
    // 添加文字
    [text drawAtPoint:point withAttributes:attributes];
    // 获取图片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    // 关闭上下文
    UIGraphicsEndImageContext();
    return newImage;
}

+ (UIImage *)waterAtImage:(UIImage *)image
               waterImgae:(UIImage *)waterImage
                     rect:(CGRect)rect {
    // 开启图形上下文
    UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
    // 绘制原图片
    [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.width)];
    // 绘制水印
    [waterImage drawInRect:rect];
    
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

参考资料

iOS绘图 - UIImage的一些简单操作(水印、裁剪、缩放、截图)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值