前言:在iOS开发中,系统已经给我们提供了功能强大的控件,可是很多并不能满足我们的需求。这时候我们需要,自定义一些美观的控件。所用的知识也就是下面的绘图。
1.1图形上下文(Graphics Context):是一个CGContextRef类型的数据
1.2图形上下文的作用
保存绘图信息、绘图状态
决定绘制的输出目标(绘制到什么地方去?)
(输出目标可以是PDF文件、Bitmap或者显示器的窗口上)
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. 绘制线条
UIGraphicsGetCurrentContext()
UIBezierPath
CGContextAddPath(ctx, path.CGPath)
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)
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;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/supersonico/article/details/46897215