绘图操作
前言:在iOS开发中,系统已经给我们提供了功能强大的控件,可是很多并不能满足我们的需求。这时候我们需要,自定义一些美观的控件。所用的知识也就是下面的绘图。
1.基本的绘图知识
1.1图形上下文
- 1.1图形上下文(Graphics Context):是一个CGContextRef类型的数据
- 1.2图形上下文的作用
保存绘图信息、绘图状态
决定绘制的输出目标(绘制到什么地方去?)
(输出目标可以是PDF文件、Bitmap或者显示器的窗口上)
1.2 - (void)drawRect:(CGRect)rect
- 作用:就是用来绘图
- 什么调用:当控件第一次显示的时候
- rect:当前控件的bounds
1.2 绘图的步骤
1.获得图形上下文:
CGContextRef ctx = UIGraphicsGetCurrentContext();
2.拼接路径
- 新建一个起点
void CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)
- 添加新的线段到某个点
void CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y)
- 添加一个矩形
void CGContextAddRect(CGContextRef c, CGRect rect)
- 添加一个椭圆
void CGContextAddEllipseInRect(CGContextRef context, CGRect rect)
- 添加一个圆弧
void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y,
CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
3.添加路径到上下文
- Mode参数决定绘制的模式
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode)
- 绘制空心路径
void CGContextStrokePath(CGContextRef c)
- 绘制实心路径
void CGContextFillPath(CGContextRef c)
提示:一般以CGContextDraw、CGContextStroke、CGContextFill开头的函数,都是用来绘制路径的
4.渲染到View上面
1.3 基本的绘图实例
1. 绘制线条
- 获得图形上下文:
UIGraphicsGetCurrentContext()
- 拼接路径:
UIBezierPath
- 添加路径到上下文
CGContextAddPath(ctx, path.CGPath)
- 渲染到View上面
CGContextStrokePath(ctx)
- (void)drawLine
{
// 一个路径对象,可以对应多跟线
// 1.获取跟当前view想关联的上下文,系统自动帮我们创建的上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 设置上下文的状态,应该放在渲染之前就可以
// 创建对应颜色对象,调用set方法
[[UIColor redColor] set];
// 设置上下文的线宽
CGContextSetLineWidth(ctx, 15);
// 设置线段的连接样式
CGContextSetLineJoin(ctx, kCGLineJoinRound);
// 设置线段的端点样式
CGContextSetLineCap(ctx, kCGLineCapRound);
// 2.拼接路径,UIBezierPath,封装好了一套很好使用的路径
UIBezierPath *path = [UIBezierPath bezierPath];
// 2.1 设置起点
[path moveToPoint:CGPointMake(10, 125)];
// 2.2 添加一根线到某个点
[path addLineToPoint:CGPointMake(220, 125)];
// 如果只使用一根路径,默认下一根线的起点在上一根跟线终点
[path addLineToPoint:CGPointMake(200, 150)];
// 3.添加路径到上下文
CGContextAddPath(ctx, path.CGPath);
// 4.渲染到view上面的图层
CGContextStrokePath(ctx);
}
2.绘制曲线
一般通过贝塞尔曲线来绘制图形:UIBezierPath
-(void)drawLIneQuadCurve
{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路径
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10, 125)];
[path addQuadCurveToPoint:CGPointMake(240, 125) controlPoint:CGPointMake(125, 0)];
// 3.路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 4.渲染上下文
//以填充的方式渲染
//CGContextFillPath(ctx);
CGContextStrokePath(ctx);
}
3.绘制矩形
// 绘制矩形
- (void)drawRect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(50, 50, 100, 100) cornerRadius:50];
CGContextAddPath(ctx, path.CGPath);
// 设置填充的颜色
[[UIColor redColor] setFill];
[[UIColor greenColor] setStroke];
// 填充:必须封闭的路径
// 即描边又填充
// 如果以后只需要描边,最好不要使用fill
CGContextDrawPath(ctx, kCGPathFillStroke);
// CGContextFillPath(ctx);
CGContextStrokePath(ctx);
}
4.绘制圆弧
- (void)drawRect:(CGRect)rect {
// 画圆弧
// center:圆心
// radius:半径
// clockwise:当前是 yes:顺时针 no:逆时针
CGPoint center = CGPointMake(125, 125);
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(125, 125) radius:100 startAngle:0 endAngle:-M_PI_2 clockwise:NO];
// 添加一根线到圆心
[path addLineToPoint:center];
// 关闭路径:从路径的终点到起点
// [path closePath];
// 填充,默认会关闭路径,从路径的终点到起点
[path fill];
}
5.绘制文字
- (void)drawRect:(CGRect)rect
{
//=========================绘制文字==========================
NSString *name = @"会长桂雏菊";
// 描述文字的属性,颜色,字体大小
NSMutableDictionary *attr = [NSMutableDictionary dictionary];
// 字体
attr[NSFontAttributeName] = [UIFont systemFontOfSize:15];
// 颜色
attr[NSForegroundColorAttributeName] = [UIColor redColor];
// 边框颜色
attr[NSStrokeColorAttributeName] = [UIColor redColor];
// 边框宽度
attr[NSStrokeWidthAttributeName] = @1;
// 阴影
NSShadow *shadow = [[NSShadow alloc] init];
shadow.shadowOffset = CGSizeMake(3, 3);
shadow.shadowColor = [UIColor yellowColor];
shadow.shadowBlurRadius = 3;
attr[NSShadowAttributeName] = shadow;
[name drawInRect:CGRectMake(90, 100, 100, 50) withAttributes:attr];
}
6.绘制饼状图
在初始化的时候时候,系统通过调用drawRect方法绘图。但是如果我们要重绘,手动调用drawRect方法是无效的。不过系统为我们准备了重绘的方法:
重绘:setNeedsDisplay
- (void)drawRect:(CGRect)rect {
// Drawing code
NSArray *datas = @[@25,@25,@50];
CGPoint center = CGPointMake(125, 125);
CGFloat r = 100;
CGFloat startA = 0;
CGFloat angle = 0;
CGFloat endA = 0;
for (NSNumber *number in datas) {
// 遍历一个数据,绘制一根扇形
startA = endA;
angle = number.intValue / 100.0 * M_PI * 2;
endA = startA + angle;
// 描述圆弧
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:r startAngle:startA endAngle:endA clockwise:YES];
[path addLineToPoint:center];
[[self randomColor] set];
[path fill];
}
}
//当点击View的时候,重绘
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self setNeedsDisplay];
}
//生成随机的颜色
- (UIColor *)randomColor
{
CGFloat r = arc4random_uniform(256) / 255.0;
CGFloat g = arc4random_uniform(256) / 255.0;
CGFloat b = arc4random_uniform(256) / 255.0;
return [UIColor colorWithRed:r green:g blue:b alpha:1];
}
7.上下文栈
将当前的上下文copy一份,保存到栈顶(那个栈叫做”图形上下文栈”):
void CGContextSaveGState(CGContextRef c)
将栈顶的上下文出栈,替换掉当前的上下文:
void CGContextRestoreGState(CGContextRef c)
- (void)drawRect:(CGRect)rect {
// Drawing code
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.拼接路径
UIBezierPath *path = [UIBezierPath bezierPath];
// 横
[path moveToPoint:CGPointMake(10, 150)];
[path addLineToPoint:CGPointMake(290, 150)];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
//================================================
// 保存上下文状态
CGContextSaveGState(ctx);
//================================================
// 设置上下文的状态
CGContextSetLineWidth(ctx, 10);
[[UIColor redColor] set];
// 4.渲染上下文,查看上下文的状态,根据状态去渲染
CGContextStrokePath(ctx);
// 竖
path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(150, 10)];
[path addLineToPoint:CGPointMake(150, 290)];
// 3.把路径添加到上下文
CGContextAddPath(ctx, path.CGPath);
// ==============================================
// 从上下文状态栈里面取出保存的状态,替换掉当前的状态
CGContextRestoreGState(ctx);
// =============================================
// 4.渲染上下文,查看上下文的状态,根据状态去渲染
CGContextStrokePath(ctx);
}
8.上下文的平移,旋转,缩放
利用矩阵操作,能让绘制到上下文中的所有路径一起发生变化
缩放
void CGContextScaleCTM(CGContextRef c, CGFloat sx, CGFloat sy)
旋转
void CGContextRotateCTM(CGContextRef c, CGFloat angle)
平移
void CGContextTranslateCTM(CGContextRef c, CGFloat tx, CGFloat ty)
- (void)drawRect:(CGRect)rect {
// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 拼接路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-100, -50, 200, 100)];
// 矩阵操作:必须要在添加路径之前进行形变
// 平移上下文
CGContextTranslateCTM(ctx, 100, 100);
// 旋转
CGContextRotateCTM(ctx, M_PI_4);
// 缩放
CGContextScaleCTM(ctx, 0.5, 0.5);
// 添加路径到上下文
CGContextAddPath(ctx, path.CGPath);
// 渲染上下文
CGContextFillPath(ctx);
}
9.图片加水印
1.开启一个基于位图的图形上下文
void UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
- size:上下文尺寸
- opaque:不透明 Yes 不透明 透明 NO
- scale:是否缩放上下文,0表示不要缩放
2.从上下文中取得图片(UIImage):
UIImage* UIGraphicsGetImageFromCurrentImageContext()
3.结束基于位图的图形上下文:
void UIGraphicsEndImageContext()
- (void)viewDidLoad {开启一个基于位图的图形上下文
void UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
[super viewDidLoad];
UIImage *image = [UIImage imageNamed:@"hina"];
// 创建位图上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
[image drawAtPoint:CGPointZero];
// 文字
NSString *str = @"会长桂雏菊";
[str drawAtPoint:CGPointMake(0, 0) withAttributes:nil];
// 根据上下文的内容生成一张图片
image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
// 用来网络中传输图片
NSData *data = UIImagePNGRepresentation(image);
[data writeToFile:@"/Users/apple/Desktop/image.png" atomically:YES];
}
10.图片裁剪
1.将当前上下所绘制的路径裁剪出来(超出这个裁剪区域的都不能显示):
void CGContextClip(CGContextRef c)
思路分析
先画一个大圆,在设置裁剪区域,把图片画上去,超出裁剪区域的自动裁剪掉。
* 加载旧图片,根据旧图片,获取上下文尺寸。
* 确定圆环宽度 borderW
* 上下文的尺寸 = 新图片的尺寸
* 确定新的上下文尺寸: newImageW : oldImageW + 2 * borderW newImageH : oldImageH + 2 * borderW,
* 绘制大圆:
1.获取上下文 2.添加路径到上下文 3.设置大圆的颜色 = 圆环的颜色 4.渲染
* 设置裁剪区域,和图片尺寸一样大,只不过,x,y不一样,x=borderW,y=borderW.
* 绘制旧图片
* 获取新图片
* 关闭上下文
* 抽分类,3个参数,图片名称,圆环宽度,圆环颜色
+ (UIImage*)imageCircleWithImage:(UIImage *)image borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor
{
// 设置圆环宽度
CGFloat boardW = borderWidth;
CGFloat imageW = image.size.width + 2 * boardW;
CGFloat imageH = image.size.height + 2 *boardW;
// 只有正方形才能正切圆,选择一个最短的尺寸,正切。
CGFloat circleW = imageW > imageH ? imageH : imageW;
CGRect rect = CGRectMake(0, 0, circleW, circleW);
// 2.开启图像上下文
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0);
// 3。获取当前上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 4.画外圆
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
CGContextAddPath(ctx, path.CGPath);
[borderColor set];
CGContextFillPath(ctx);
// 设置头像尺寸
rect = CGRectMake(boardW, boardW, image.size.width , image.size.height);
// 5.创建裁剪路径
UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:rect];
// 6.裁剪路径
// 根据这个路径进行裁剪,超出路径以外的部分就不会显示了
[clipPath addClip];
// 7.画头像
[image drawInRect:rect];
// 不能直接在这返回,上下文没有关闭,会消耗内存.
// 8.获取新图片
image = UIGraphicsGetImageFromCurrentImageContext();
// 9.关闭上下文
UIGraphicsEndImageContext();
return image;
}
11.截取屏幕
View之所以能显示东西,完全是因为它内部的layer。View内部有个layer(图层)属性,drawRect:方法中取得的是一个Layer Graphics Context,因此,绘制的东西其实是绘制到view的layer上去了
- (void)renderInContext:(CGContextRef)ctx;
//截取屏幕
+ (UIImage*)imageWithCaptureView:(UIView*)captureView;
{
// 1.开启上下文
UIGraphicsBeginImageContextWithOptions(captureView.bounds.size, NO, 0.0);
// 2.获取当前上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 3.把控制器图层渲染到上下文
[captureView.layer renderInContext:ctx];
// 4.取出新图片
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。