iOS 图形绘制

需求

在许多需要图形显示数据的应用中常常需要进行图形绘制,例如股票金融。在一些特定动画中,我们同样需要结合图形的绘制。

重绘视图

UIView 视图中有一个用来绘制当前图形的方法 -drawRect 方法。我们可以重写该方法,将需要的图形绘制到当前上线文。绘制图形一般有两种方式:CGContextRefUIBezierPath

CGContextRef 篇

CGContextRef 属于 Core Graphics 框架,功能强大,我们借助它可以绘制直线、曲线、多边形圆形等各种各样的几何图形,也可以绘制文字、图像等。

Graphics Context 是图形上下文,可以将其理解为一块画布,我们可以在上面进行绘画操作,绘制完成后,将画布放到我们的view中显示即可,view看作是一个画框,CGContextRef 看成 Quartz 2D 绘图环境。

  • 使用步骤

1.先在 drawRect 方法中获得上下文 context
2.绘制图形(线,图形,图片等)
3.设置一些修饰属性
4.渲染到上下文,完成绘图

drawRect方法什么时候触发?

view第一次显示到屏幕上时,或者主动调用 setNeedsDisplay 或者 setNeedsDisplayInRect:方法时。

  • 绘图样式
// 矩形
//1.获取图形上下文,即画板
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.绘制图形
CGContextAddRect(ctx, CGRectMake(10, 10, 40, 40));
//3.设置相关属性
[[UIColor redColor] setStroke];    //颜色 CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextSetLineWidth(ctx, 5);     //线条到宽度
//4.渲染到画板上
CGContextStrokePath(ctx);          //描边
//CGContextFillPath(ctx);          //填充


/**以下为核心部分**/
//圆弧、圆
CGContextAddArc(ctx, 100, 30, 30, 0, M_PI, 0); //最后一个参数表示方向(顺时针或者逆时针)

//椭圆
CGContextAddEllipseInRect(ctx, CGRectMake(150, 10, 80, 40)); //在矩形内绘制椭圆,宽度和高度相等则为圆形

//直线
CGContextMoveToPoint(ctx, 10, 100);                     //起点
CGContextAddLineToPoint(ctx, kScreenWidth-10, 100);     //其他点
CGContextSetRGBStrokeColor(ctx, 0, 1, 0, 1);            //RGB类型的颜色
CGContextSetLineCap(ctx, kCGLineCapSquare);       		//起点和重点
CGContextSetLineJoin(ctx, kCGLineJoinBevel);            //转角
CGContextSetLineWidth(ctx, 5);                          //线条的宽度

//不规则图形
CGContextMoveToPoint(ctx, 20, 150);           	//设置起点                     
CGContextAddLineToPoint(ctx, 100, 180);			//
CGContextClosePath(ctx); 
CGContextSetLineWidth(ctx, 5);  				//线宽
CGContextSetRGBStrokeColor(ctx, 0, 1, 0, 1);    //RGB类型的颜色
CGContextSetLineCap(ctx, kCGLineCapSquare);     //终点样式
CGContextSetLineJoin(ctx, kCGLineJoinBevel);    //连接点样式
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);  //设置填充色
CGContextSetStrokeColorWithColor(ctx, [UIColor brownColor].CGColor);  //路径颜色
/*
 kCGPathFill,           //填充
 kCGPathEOFill,        
 kCGPathStroke,         //线条
 kCGPathFillStroke,     //线条+填充
 kCGPathEOFillStroke
*/
CGContextDrawPath(ctx, kCGPathFillStroke);      //fill  and  stroke

//绘制图片
 UIImage *img = [UIImage imageNamed:@"QQ"];
[img drawAtPoint:CGPointMake( 150, 125)];    //绘制到指定的点,图片size
//[img drawInRect:CGRectMake(120, 140, img.size.width, img.size.height)];       //指定位置大小
//[img drawAsPatternInRect:CGRectMake(0, 0, kScreenWidth, kScreenHeight/2.0)];      //多个平铺

//绘制文字
NSString *str = @"少年不识愁滋味,为赋新词强说愁\n而今识得愁滋味,却道天凉好个秋";
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
style.alignment = NSTextAlignmentCenter;
NSDictionary *dic = @{NSForegroundColorAttributeName:[UIColor redColor],
                          NSFontAttributeName:[UIFont systemFontOfSize:18],
                          NSParagraphStyleAttributeName:style};
CGSize size = [str sizeWithAttributes:dic];
CGRect strRect = CGRectMake((kScreenWidth-size.width)/2.0, 200, size.width, size.height);
[str drawWithRect:strRect options:NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil];

UIBezierPath 篇

UIBezierPath 这个类在 UIKit 中, 是 Core Graphics 框架关于 path 的一个面向对象的封装,使用此类可以定义简单的形状,比如我们常用到,矩形,圆形,椭圆,弧,或者不规则的多边形。

UIBezierPath 使用起来非常的方便,创建一个路径对象,然后调用其相关的属性和方法即可完成绘制。

  • 使用步骤

1、 重写View的drawRect方法
2、 创建UIBezierPath的对象
3、 使用相关的方法创建路径信息
4、 使用stroke 或者 fill方法结束绘图

  • 绘制样式

首先是初始化方式。

// 空路径
UIBezierPath *path = [UIBezierPath bezierPath];
// 矩形路径
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(10, 20, 40, 40)];
// 椭圆路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(80, 20, 80, 40)];
// 圆角矩形路径
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(200, 20, 50, 50) cornerRadius:10];
// 圆弧路径,参数:圆弧圆心、圆弧半径、开始弧度、结束弧度、是否为顺时针
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(180, 150) radius:50 startAngle:M_PI endAngle:0 clockwise:YES];

你可以使用 path 进行接下来的图形绘制。

  1. 绘制直线
// 1、初始化
UIBezierPath *path = [UIBezierPath bezierPath];
// 2、设置路径
[path moveToPoint:CGPointMake(250, 150)];	//起点
[path addLineToPoint:CGPointMake(kScreenWidth-10, 150)];   //移动到下一个点
// 3、设置属性
//[[UIColor redColor] setFill];  //设置填充色
//[UIColor redColor] setStroke];   //设置线条颜色
// 4、渲染绘图
[path stroke]; //描边
// [path fill]; //填充
  1. 绘制圆弧

坐标系
圆弧坐标系

// 设置起点
[path moveToPoint:CGPointMake(20, 200)];	
// 圆弧圆心、圆弧半径、开始弧度、结束弧度、是否为顺时针
[path addArcWithCenter:CGPointMake(80, 200) radius:40 startAngle:M_PI endAngle:0 clockwise:NO];
  1. 二次贝塞尔曲线

二次贝塞尔曲线
二次贝塞尔曲线需要一个控制点,两个端点。控制点与两个端点的连线分别是圆弧的两条切线。

// 设置起点
[path moveToPoint:CGPointMake(160, 200)];
// 曲线的终点位置、控制点
[path addQuadCurveToPoint:CGPointMake(200, 200) controlPoint:CGPointMake(170, 150)];
  1. 三次贝塞尔曲线

三次贝塞尔曲线

// 起点
[path moveToPoint:CGPointMake(220, 230)];
// 曲线的终点位置、第一控制点、第二控制点
[path addCurveToPoint:CGPointMake(300, 180) controlPoint1:CGPointMake(240, 100) controlPoint2:CGPointMake(250, 300)];
  • 其他的属性或方法
// 路径的只读属性
empty //是否为空
bounds //封闭所有路径点的最小矩形范围, 不包括控制点
currentPoint //当前的点

// 可以设置的属性
lineWidth //宽度
lineCapStyle //起点终点的样式
/*
kCGLineCapButt,    // 默认 (方形结束, 结束位置正好为精确位置),如果线条太宽,看起来不完全封闭
kCGLineCapRound,   // (圆形结束, 结束位置超过精确位置半个线宽)
kCGLineCapSquare   // (方形结束, 结束位置超过精确位置半个线宽)
*/
lineJoinStyle //线条连接点样式
/*
kCGLineJoinMiter,    // 默认
kCGLineJoinRound,    // 圆形
kCGLineJoinBevel     // 
*/

-closePath //封闭路径,可以自动连接起点和终点
-removeAllPoints //移除路径中所有点
-appendPath: //添加一条路径
-bezierPathByReversingPath //路径反转  当前路径的终点作为起点
-applyTransform: //使用CGAffineTransform
-containsPoint: //路径区域中是否包含某个点

外部绘制

通常在自定义视图中重写 -drawrect: 方法即可完成视图的重新绘制,也是我比较推荐的。除了重写方法外,在视图外部也是可以实现图形绘制,比如设置图层 CALayer 代理、使用 CALayer 的子类 CAShapeLayer

图层代理

每一个视图都会拥有自己的图层 CALayer 这图层就是用来显示绘制图像的,而视图用来布局,响应处理用户的触摸、点击、滚动等交互事件。 图层有一个协议 CALayerDelegate ,该协议中可以将图层的上下对象 CGContextRef 传递过来,我们只需要在该代理方法中完成绘制即可。

  • 使用步骤

设置图层代理
在代理方法中完成绘制
调用图层 setNeedsDisplay

  • 演示示例
- (void)viewDidLoad {
    [super viewDidLoad];
    UIView* drawView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    drawView.backgroundColor = UIColor.grayColor;
    drawView.center = self.view.center;
    drawView.layer.delegate = self; // 设置图层代理
    [drawView.layer setNeedsDisplay]; // 一定要调用
    [self.view addSubview:drawView];
}
// CALayer的代理,ctx是绘制的上下文
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
    CGContextAddRect(ctx, CGRectMake(10, 10, 40, 40));
    [[UIColor redColor] setStroke];
    CGContextSetLineWidth(ctx, 2);
    CGContextStrokePath(ctx);
}

CAShapeLayer

CAShapeLayer 是图层 CALayer 的子类,是和图形相关的图层,单个的 CAShapeLayer 是没有意义的,需要配合路径对象 UIBezierPath 使用。CAShapeLayer 可以看作是 CGContextRef 的面向对象封装,一块图层画板。UIBezierPath 就是这块画板的具体展示。

  • 使用步骤

创建 UIBezierPath 对象并添加路径
创建 CAShapeLayer 对象,设置图形属性,如颜色
将 UIBezierPath 对象传递给 CAShapeLayer 对象。
将 CAShapeLayer 添加到视图的图层上显示

  • 演示示例
// 路径
UIBezierPath* path = [UIBezierPath bezierPathWithRect:CGRectMake(100, 100, 100, 100)];
CAShapeLayer* shapeLayer = [CAShapeLayer layer];
// 配置相关属性
shapeLayer.strokeColor = UIColor.redColor.CGColor;
shapeLayer.fillColor = UIColor.clearColor.CGColor;
// 路径传递给 shapeLayer
shapeLayer.path = path.CGPath;
// 添加到视图的图层上
[self.view.layer addSublayer:shapeLayer];

更多关于 CAShapeLayer 的使用示例可以参考这篇

CGContextRef 和 UIBezierPath+CAShapeLayer 对比

CGContextRef 属于 CoreGraphic 框架,使用 CPU 渲染图形,消耗内存较大,CAShapeLayer 属于 CoreAnimation ,通过 GPU 渲染图形,优化性能,动画相关的渲染同样交给了 GPU,不消耗内存。UIBezierPath+CAShapeLayer 方式是面向对象的方式绘图,符合 Ojective-C 面向对象的编程思想。

CoreGraphics 是底层绘制框架,CoreAnimation 中大量使用到CoreGraphics中的类,是一套基于 CoreGraphics 的 Ojective-C 语言封装。

总结

图形可以通过 CGContextRef 进行绘制,绘制方式有两种,一种是自定义视图并重写绘制方法 -drawrect: ,另一种是设置图层代理 delegate 在视图外部绘制。CGContextRef 使用 CPU 来进行图像渲染,消耗大。UIBezierPath+CAShapeLayer 同样可以用来绘制图形,是 Ojective-C 针对 CGContextRef 面向对象的一种封装,这种通过 CAShapeLayer 和 path 的形式灵活多变,并且使用 GPU 直接进行绘制和动画渲染,相比与传统的 CGContextRef,这种方式比较适合 iOS 中的图形绘制。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值