内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

iOS重绘之drawRect

2018-12-03 21:06 出处:清屏网 人气: 评论(0

drawRect是 UIView 类的一个方法,在drawRect中所调用的重绘功能是基于 Quartz 2D 实现的, Quartz 2D 是一个二维图形绘制引擎,支持 iOS 环境和 Mac OS X 环境。利用 UIKit 框架提供的控件,我们能实现一些简单的UI界面,但是,有些UI界面比较复杂,用普通的UI控件无法实现,或者实现效果不佳,这时可以利用 Quartz 2D 技术将控件内部的结构画出来,自定义所需控件,这也是 Quartz 2D 框架在 iOS 开发中一个很重要的价值。

iOS的绘图操作是在UIView类的drawRect方法中进行的,我们可以 重写 一个view的drawRect方法,在其中进行绘图操作,在首次显示该view时程序会自动调用此方法进行绘图。 在多次手动重复绘制的情况下,需要调用UIView中的 setNeedsDisplay 方法,则程序会自动调用drawRect方法进行重绘。 PS:苹果官网关于drawRect的介绍

二、drawRect的使用过程

在view的 drawRect 方法中,利用 Quartz 2D 提供的API绘制图形的步骤:

1)新建一个 view ,继承自UIView,并 重写 drawRect 方法;

2)在 drawRect 方法中,获取图形上下文;

3)绘图操作;

4)渲染。

三、何为CGContext?

Quartz 2DCoreGraphics 框架的一部分,因此其中的相关类及方法都是以CG为前缀。在drawRect重绘过程中最常用的就是 CGContext 类。 CGContext 又叫图形上下文,相当于一块画板,以堆栈形式存放,只有在当前 context 上绘图才有效。iOS又分多种图形上下文,其中UIView自带提供的在drawRect方法中通过 UIGraphicsGetCurrentContext 获取,还有专门为图片处理的context,还有 pdf 的context等等均有特定的获取方法,本文只对第一种做相关介绍。

CGContext 类中的常用方法:

// 获取当前上下文
CGContextRef context = UIGraphicsGetCurrentContext(); 

// 移动画笔
CGContextMoveToPoint 
// 在画笔位置与point之间添加将要绘制线段 (在draw时才是真正绘制出来)
CGContextAddLineToPoint 
// 绘制椭圆
CGContextAddEllipseInRect 
CGContextFillEllipseInRect
// 设置线条末端形状
CGContextSetLineCap 
// 画虚线
CGContextSetLineDash 
// 画矩形
CGContextAddRect 
CGContextStrokeRect 
CGContextStrokeRectWithWidth 
// 画一些线段
CGContextStrokeLineSegments 

// 画弧: 以(x1, y1)为圆心radius半径,startAngle和endAngle为弧度
CGContextAddArc(context, x1, y1, radius, startAngle, endAngle, clockwise);
// 先画两条线从point 到 (x1, y1) , 从(x1, y1) 到(x2, y2) 的线  切里面的圆
CGContextAddArcToPoint(context, x1, y1,  x2,  y2, radius);

// 设置阴影
CGContextSetShadowWithColor 
// 设置填充颜色
CGContextSetRGBFillColor 
// 设置画笔颜色
CGContextSetRGBStrokeColor 
// 设置填充颜色空间
CGContextSetFillColorSpace 
// 设置画笔颜色空间
CGConextSetStrokeColorSpace 
// 以当前颜色填充rect
CGContextFillRect 
// 设置透明度
CGContextSetAlaha 

// 设置线的宽度
CGContextSetLineWidth 
// 画多个矩形
CGContextAddRects 
// 画曲线
CGContextAddQuadCurveToPoint 
// 开始绘制图片
CGContextStrokePath
// 设置绘制模式 
CGContextDrawPath 
// 封闭当前线路
CGContextClosePath 
// 反转画布
CGContextTranslateCTM(context, 0, rect.size.height);   CGContextScaleCTM(context, 1.0, -1.0);
// 从原图片中取小图
CGImageCreateWithImageInRect 

// 画图片
CGImageRef image=CGImageRetain(img.CGImage);
CGContextDrawImage(context, CGRectMake(10.0, height - 100.0, 90.0, 90.0), image);

// 实现渐变颜色填充
CGContextDrawLinearGradient(context, gradient, CGPointMake(0.0, 0.0) ,CGPointMake(0.0, self.frame.size.height), kCGGradientDrawsBeforeStartLocation);

复制代码

四、用drawRect方法重绘的实例

我们在drawRect方法中绘制一些图形,如图:

代码实现如下:

- (void)drawRect:(CGRect)rect {
    
    //1. 注:如果没有获取context时,是什么都不做的(背景无变化)
    [super drawRect:rect];
    
    // 获取上下文
    CGContextRef context =UIGraphicsGetCurrentContext();
    CGSize size = rect.size;
    CGFloat offset = 20;
    
    // 画脑袋
    CGContextSetRGBStrokeColor(context,1,1,1,1.0);
    CGContextSetLineWidth(context, 1.0);
    CGContextAddArc(context, size.width / 2, offset + 30, 30, 0, 2*M_PI, 0);
    CGContextDrawPath(context, kCGPathStroke);
    
    // 画眼睛和嘴巴
    CGContextMoveToPoint(context, size.width / 2 - 23, 40);
    CGContextAddArcToPoint(context, size.width / 2 - 15, 26, size.width / 2 - 7, 40, 10);
    CGContextStrokePath(context);
    
    CGContextMoveToPoint(context, size.width / 2 + 7, 40);
    CGContextAddArcToPoint(context, size.width / 2 + 15, 26, size.width / 2 + 23, 40, 10);
    CGContextStrokePath(context);//绘画路径
    
    CGContextMoveToPoint(context, size.width / 2 - 8, 65);
    CGContextAddArcToPoint(context, size.width / 2, 80, size.width / 2 + 8, 65, 10);
    CGContextStrokePath(context);//绘画路径
    
    // 画鼻子
    CGPoint nosePoints[3];
    nosePoints[0] = CGPointMake(size.width / 2, 48);
    nosePoints[1] = CGPointMake(size.width / 2 - 3, 58);
    nosePoints[2] = CGPointMake(size.width / 2 + 3, 58);
    CGContextAddLines(context, nosePoints, 3);
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFillStroke);
    
    // 画脖子
    CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
    CGContextStrokeRect(context, CGRectMake(size.width / 2 - 5, 80, 10, 10));
    CGContextFillRect(context,CGRectMake(size.width / 2 - 5, 80, 10, 10));
    
//    // 画衣裳
//    CGPoint clothesPoints[4];
//    clothesPoints[0] = CGPointMake(size.width / 2 - 30, 90);
//    clothesPoints[1] = CGPointMake(size.width / 2 + 30, 90);
//    clothesPoints[2] = CGPointMake(size.width / 2 + 100, 200);
//    clothesPoints[3] = CGPointMake(size.width / 2 - 100, 200);
//    CGContextAddLines(context, clothesPoints, 4);
//    CGContextClosePath(context);
//    CGContextDrawPath(context, kCGPathFillStroke);
    
    // 衣裳颜色渐变
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, size.width / 2 - 30, 90);
    CGPathAddLineToPoint(path, NULL, size.width / 2 + 30, 90);
    CGPathAddLineToPoint(path, NULL, size.width / 2 + 100, 200);
    CGPathAddLineToPoint(path, NULL, size.width / 2 - 100, 200);
    CGPathCloseSubpath(path);
    [self drawLinearGradient:context path:path startColor:[UIColor cyanColor].CGColor endColor:[UIColor yellowColor].CGColor];
    CGPathRelease(path);
    
    // 画胳膊
    CGContextSetFillColorWithColor(context, [UIColor colorWithRed:0 green:1 blue:1 alpha:1].CGColor);
    CGContextMoveToPoint(context, size.width / 2 - 28, 90);
    CGContextAddArc(context, size.width / 2 - 28, 90, 80,  - M_PI, -1.05 * M_PI, 1);
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFill);
    CGContextMoveToPoint(context, size.width / 2 + 28, 90);
    CGContextAddArc(context, size.width / 2 + 28, 90, 80,  0, 0.05 * M_PI, 0);
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFill);
    
    // 画左手
    CGPoint aPoints[2];
    aPoints[0] =CGPointMake(size.width / 2 - 30 - 81, 90);
    aPoints[1] =CGPointMake(size.width / 2 - 30 - 86, 90);
    CGContextAddLines(context, aPoints, 2);
    aPoints[0] =CGPointMake(size.width / 2 - 30 - 80, 93);
    aPoints[1] =CGPointMake(size.width / 2 - 30 - 85, 93);
    CGContextAddLines(context, aPoints, 2);
    CGContextDrawPath(context, kCGPathStroke);
    // 画右手
    aPoints[0] =CGPointMake(size.width / 2 + 30 + 81, 90);
    aPoints[1] =CGPointMake(size.width / 2 + 30 + 86, 90);
    CGContextAddLines(context, aPoints, 2);
    aPoints[0] =CGPointMake(size.width / 2 + 30 + 80, 93);
    aPoints[1] =CGPointMake(size.width / 2 + 30 + 85, 93);
    CGContextAddLines(context, aPoints, 2);
    CGContextDrawPath(context, kCGPathStroke);
    
//    // 画虚线
//    aPoints[0] =CGPointMake(size.width / 2 + 30 + 81, 90);
//    aPoints[1] =CGPointMake(size.width / 2 + 30 + 86, 90);
//    CGContextAddLines(context, aPoints, 2);
//    aPoints[0] =CGPointMake(size.width / 2 + 30 + 80, 93);
//    aPoints[1] =CGPointMake(size.width / 2 + 30 + 85, 93);
//    CGContextAddLines(context, aPoints, 2);
//    CGFloat arr[] = {1, 1};
//    CGContextSetLineDash(context, 0, arr, 2);
//    CGContextDrawPath(context, kCGPathStroke);
    
    // 画双脚
    CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
    CGContextAddEllipseInRect(context, CGRectMake(size.width / 2 - 30, 210, 20, 15));
    CGContextDrawPath(context, kCGPathFillStroke);
    CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
    CGContextAddEllipseInRect(context, CGRectMake(size.width / 2 + 10, 210, 20, 15));
    CGContextDrawPath(context, kCGPathFillStroke);
    
    // 绘制图片
    UIImage *image = [UIImage imageNamed:@"img_watch"];
    [image drawInRect:CGRectMake(60, 270, 100, 120)];
    //[image drawAtPoint:CGPointMake(100, 340)];
    //CGContextDrawImage(context, CGRectMake(100, 340, 20, 20), image.CGImage);
    
    // 绘制文字
    UIFont *font = [UIFont boldSystemFontOfSize:20.0];
    NSDictionary *attriDict = @{NSFontAttributeName:font, NSForegroundColorAttributeName:[UIColor redColor]};
    [@"绘制文字" drawInRect:CGRectMake(180, 270, 150, 30) withAttributes:attriDict];
}

- (void)drawLinearGradient:(CGContextRef)context
                      path:(CGPathRef)path
                startColor:(CGColorRef)startColor
                  endColor:(CGColorRef)endColor {
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat locations[] = { 0.0, 1.0 };
    NSArray *colors = @[(__bridge id) startColor, (__bridge id) endColor];
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
    CGRect pathRect = CGPathGetBoundingBox(path);
    //具体方向可根据需求修改
    CGPoint startPoint = CGPointMake(CGRectGetMidX(pathRect), CGRectGetMinY(pathRect));
    CGPoint endPoint = CGPointMake(CGRectGetMidX(pathRect), CGRectGetMaxY(pathRect));
    CGContextSaveGState(context);
    CGContextAddPath(context, path);
    CGContextClip(context);
    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
    CGContextRestoreGState(context);
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
}
复制代码

注:

1)当view未设置背景颜色时,重绘区域的背景颜色默认为‘黑’;

2)设置画笔颜色的方法 CGContextSetRGBStrokeColor ,设置填充颜色的方法 CGContextSetFillColorWithColor

3)每次绘制独立的图形结束时,都要实时调用 CGContextDrawPath 方法来将这个独立的图形绘制出来,否则多次 CGContextMoveToPoint 会使绘制的图形乱掉;

4)区别 CGContextAddArcCGContextAddArcToPoint

5)画虚线时,之后所有的线条均变成虚线(除非再手动设置成是实现)

五、CAShapeLayer绘图与drawRect重绘的比较

在网上查了一些 CAShapeLayerdrawRect 重绘的一些比较,整理如下,有助于我们学习与区分:

(1)两种自定义控件样式的方法各有优缺点, CAShapeLayer 配合贝赛尔曲线使用时,绘图形状更灵活,而 drawRect 只是一个方法而已,在其中更适合绘制大量有规律的通用的图形;

(2) CALayer 的属性变化默认会有动画, drawRect 绘图没有动画;

(3) CALayer 绘制图形是实时的, drawRect 多次重绘需要手动调用 setNeedsLayout

(4)性能方面, CAShapeLayer 使用了硬件加速,绘制同一图形会比用 Core Graphics 快很多, CAShapeLayer 属于 CoreAnimation 框架,动画渲染直接提交给手机 GPU ,不消耗内,而 Core Graphics 会消耗大量的 CPU 资源。

另外,源码中还通过重绘实现了两个简单的排序算法, 工程源码GitHub地址


分享给小伙伴们:
本文标签: drawRectiOS

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号