iOS中 CoreGraphics快速绘图(详解)

第一步:先科普一下基础知识:

Core Graphics是基于C的API,可以用于一切绘图操作

Core Graphics 和Quartz 2D的区别

quartz是一个通用的术语,用于描述在IOS和MAC OS X ZHONG 整个媒体层用到的多种技术 包括图形、动画、音频、适配。

Quart 2D 是一组二位绘图和渲染API,Core Graphic会使用到这组API

Quartz Core 专指Core Animation用到的动画相关的库、API和类

点和像素的对比

系统拥有坐标系,如320*480 硬件有retain屏幕和非retain屏:如320*480、640*960

Core Graphics 使用的是系统的坐标系来绘制图片。在分辨率为640*960手机上绘制图片时,实际上Core Graphics 的坐标是320*480。这个时候每个坐标系上的点,实际上拥有两个像素。

图形上下文

Core Graphics 使用图形上下文进行工作,这个上下文的作用像画家的画布一样。

在图形上下文之外是无法绘图的,我们可以自己创建一个上下文,但是性能和内存的使用上,效率是非常低得。

我们可以通过派生一个UIView的子类,获得它的上下文。在UIView中调用drawRect:方法时,会自动准备好一个图形上下文,可以通过调用

UIGraphicsGetCurrentContext()来获取。 因为它是运行期间绘制图片,我们可以动态的做一些额外的操作

Core Graphics的优点

快速、高效,减小应用的文件大小。同时可以自由地使用动态的、高质量的图形图像。 使用Core Graphics,可以创建直线、路径、渐变、文字与图像等内容,并可以做变形处理。

绘制自定义视图

drawRect:是系统的方法,不要从代码里面直接调用 drawRect:,而应该使用setNeedsDisplay重绘.

需要知道的术语

路径 path

阴影 shadow

笔画 stroke

剪裁路径 Clip Path

线条粗细 Line Width

混合模式 Blend Mode

填充色 Fill Color

当前形变矩阵 Current Transform Matrix

线条图案 Line Dash

图形上下文

一个图形上下文好比是画布上的一副扁平的图画 执行绘画动作,这些动作是在同一个图层上完成的。 图形上下文不允许将内容分不到多个图层中,如果有需求在不同图层上画,可以考虑使用视图层次结构,创建多个UIView,并将他们作为父视图的子视图

图形上下文栈可以把图形上下文的当前状态保存下来,并在执行一些动作后再次恢复回来

CGContextSaveGState();

CGContextStoreGState();

路径、渐变、文字和图像

1. 使用UIBezierPath创建路径

2. 手动创建路径 moveToPoint addLineToPoint addArcWithCenter addCurveToPoint

渐变,渐变可以在指定方向上,以可变的比率在一系列颜色之间转化

线性渐变:沿着一条定义好了起点和重点的直线方向,呈线性变化。如果这条线有一定角度,线性渐变也会沿相同路径变化

放射渐变:颜色顺着两个原型之间的方向线性变化,这两个园为起始圆和终止圆,每隔圆都有自己的圆心和班级

文字

darwAtPoint

drawInRect

图像

Core Graphics 不会保持图像的长宽比例,Core Graphics会将图像的边界设置为CGrect,不管图片是否变形 darwAtPoint drawInRect

第二步:代码部分:

基础画法就不多讲啦!都通用:

第一种绘图形式:在UIView的子类方法drawRect:中绘制一个蓝色圆,使用UIKit在Cocoa为我们提供的当前上下文中完成绘图任务。

 - (void) drawRect: (CGRect) rect {

UIBezierPath* p = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0,100,100)];

[[UIColor blueColor] setFill];

[p fill];

}

第二种绘图形式:使用Core Graphics实现绘制蓝色圆。

 - (void) drawRect: (CGRect) rect {

CGContextRef con = UIGraphicsGetCurrentContext();

CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));

CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);

CGContextFillPath(con);

}

第三种绘图形式:我将在UIView子类的drawLayer:inContext:方法中实现绘图任务。drawLayer:inContext:方法是一个绘制图层内容的代理方法。为了能够调用drawLayer:inContext:方法,我们需要设定图层的代理对象。但要注意,不应该将UIView对象设置为显示层的委托对象,这是因为UIView对象已经是隐式层的代理对象,再将它设置为另一个层的委托对象就会出问题。轻量级的做法是:编写负责绘图形的代理类。在MyView.h文件中声明如下代码:

 @interfaceMyLayerDelegate : NSObject

@end

然后MyView.m文件中实现接口代码:

 @implementationMyLayerDelegate

- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx {

UIGraphicsPushContext(ctx);

UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)];

[[UIColor blueColor] setFill];

[p fill];

UIGraphicsPopContext();

}

@end

直接将代理类的实现代码放在MyView.m文件的#import代码的下面,这样感觉好像在使用私有类完成绘图任务(虽然这不是私有类)。需要注意的是,我们所引用的上下文并不是当前上下文,所以为了能够使用UIKit,我们需要将引用的上下文转变成当前上下文。

因为图层的代理是assign内存管理策略,那么这里就不能以局部变量的形式创建MyLayerDelegate实例对象赋值给图层代理。这里选择在MyView.m中增加一个实例变量,因为实例变量默认是strong:

 @interfaceMyView () {

MyLayerDelegate* _layerDeleagete;

}

@end

使用该图层代理:

 MyView *myView = [[MyView alloc] initWithFrame: CGRectMake(0,0,320,480)];

CALayer *myLayer = [CALayer layer];

_layerDelegate = [[MyLayerDelegate alloc] init];

myLayer.delegate = _layerDelegate;

[myView.layer addSublayer:myLayer];

[myView setNeedsDisplay];// 调用此方法,drawLayer: inContext:方法才会被调用。

第四种绘图形式:使用Core Graphics在drawLayer:inContext:方法中实现同样操作,代码如下:


 - (void)drawLayer:(CALayer*)lay inContext:(CGContextRef)con {

CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));

CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);

CGContextFillPath(con);

}

最后,演示UIGraphicsBeginImageContextWithOptions的用法,并从上下文中生成一个UIImage对象。生成UIImage对象的代码并不需要等待某些方法被调用后或在UIView的子类中才能去做。

第五种绘图形式:使用UIKit实现:

 UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO,0);

UIBezierPath* p = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0,0,100,100)];

[[UIColor blueColor] setFill];

[p fill];

UIImage* im = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

解释一下UIGraphicsBeginImageContextWithOptions函数参数的含义:第一个参数表示所要创建的图片的尺寸;第二个参数用来指定所生成图片的背景是否为不透明,如上我们使用YES而不是NO,则我们得到的图片背景将会是黑色,显然这不是我想要的;第三个参数指定生成图片的缩放因子,这个缩放因子与UIImage的scale属性所指的含义是一致的。传入0则表示让图片的缩放因子根据屏幕的分辨率而变化,所以我们得到的图片不管是在单分辨率还是视网膜屏上看起来都会很好。

第六种绘图形式:使用Core Graphics实现:

 UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO,0);

CGContextRef con = UIGraphicsGetCurrentContext();

CGContextAddEllipseInRect(con, CGRectMake(0,0,100,100));

CGContextSetFillColorWithColor(con, [UIColor blueColor].CGColor);

CGContextFillPath(con);

UIImage* im = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

第三步:实践部分:

第一种:基本图形绘制

 /**

*  什么调用:当你视图第一次显示的时候就会调用

*  作用:绘图

*  @param rect = self.bounds

*/

- (void)drawRect:(CGRect)rect

{

// 1.获取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路径

UIBezierPath *path = [UIBezierPath bezierPath];

CGPoint startP = CGPointMake(10,125);

CGPoint endP = CGPointMake(240,125);

CGPoint controlP = CGPointMake(125,0);

[path moveToPoint:startP];

[path addQuadCurveToPoint:endP controlPoint:controlP];

// 3.把路径添加到上下文

CGContextAddPath(ctx, path.CGPath);

// 4.渲染上下文到视图

CGContextStrokePath(ctx);

}

- (void)drawLine

{

// 1.获取上下文

// CGContextRef CG CoreGraphics Ref 引用

// 目前学的上下文都跟UIGraphics有关,以后想直接获取上下文,直接敲一个UIGraphics

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.设置绘图信息(拼接路径)

UIBezierPath *path = [UIBezierPath bezierPath];

// 设置起点

[path moveToPoint:CGPointMake(10,10)];

// 添加一条线到某个点

[path addLineToPoint:CGPointMake(125,125)];

[path addLineToPoint:CGPointMake(240,10)];

// 3.把路径添加到上下文

// 直接把UIKit的路径转换成CoreGraphics,CG开头就能转

CGContextAddPath(ctx, path.CGPath);

// 4.把上下文渲染到视图

// Stroke描边

CGContextStrokePath(ctx);

}

- (void)draw2Line

{

// 1.获取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路径

UIBezierPath *path = [UIBezierPath bezierPath];

// 设置起点

[path moveToPoint:CGPointMake(10,125)];

// 添加一条线到某个点

[path addLineToPoint:CGPointMake(230,125)];

//    // 设置起点

//    [path moveToPoint:CGPointMake(10, 10)];

//

//    // 添加一条线到某个点

//    [path addLineToPoint:CGPointMake(125, 100)];

UIBezierPath *path1 = [UIBezierPath bezierPath];

[path1 moveToPoint:CGPointMake(10,10)];

[path1 addLineToPoint:CGPointMake(125,100)];

// 3.把路径添加到上下文

CGContextAddPath(ctx, path.CGPath);

CGContextAddPath(ctx, path1.CGPath);

// 设置绘图状态

// 设置线宽

CGContextSetLineWidth(ctx,10);

CGContextSetLineCap(ctx, kCGLineCapRound);

//    CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);

[[UIColor redColor] set];

// 4.渲染上下文到视图

CGContextStrokePath(ctx);

}

第二种:下载进度条:

 - (void)setProgress:(CGFloat)progress

{

_progress = progress;

self.myLabel.text = [NSString stringWithFormat:@"%.2f%%",progress*100];

//    [self drawRect:self.bounds];

// 重新绘制

// 在view上做一个重绘的标记,当下次屏幕刷新的时候,就会调用drawRect.

[self setNeedsDisplay];

}

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

// 当视图显示的时候会调用 默认只会调用一次

- (void)drawRect:(CGRect)rect

{

// 1.获取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路径

CGPoint center = CGPointMake(50,50);

CGFloat radius =50-2;

CGFloat startA = -M_PI_2;

CGFloat endA = -M_PI_2 + _progress * M_PI *2;

UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

// 3.把路径添加到上下文

CGContextAddPath(ctx, path.CGPath);

// 4.把上下文渲染到视图

CGContextStrokePath(ctx);

}

第三种:饼图

 - (void)drawRect:(CGRect)rect

{

// Drawing code

NSArray *data = @[@25,@25,@50];

// 1.获取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路径

CGPoint center = CGPointMake(125,125);

CGFloat radius =120;

CGFloat startA =0;

CGFloat angle =0;

CGFloat endA =0;

for(NSNumber *number in data) {

// 2.拼接路径

startA = endA;

angle = number.intValue /100.0* M_PI *2;

endA = startA + angle;

UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

[path addLineToPoint:center];

[[UIColor randomColor] set];

// 把路径添加上下文

CGContextAddPath(ctx, path.CGPath);

// 渲染

CGContextFillPath(ctx);

}

}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

CGFloat a = arc4random_uniform(6);

//CGFloat a =  arc4random()%6;

NSLog(@"随机数--%f",a);

[self setNeedsDisplay];

}

- (void)drawPie

{

// 1.获取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 2.拼接路径

CGPoint center = CGPointMake(125,125);

CGFloat radius =120;

CGFloat startA =0;

CGFloat angle =0;

CGFloat endA =0;

// 第一个扇形

angle =25/100.0* M_PI *2;

endA = startA + angle;

UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

[path addLineToPoint:center];

// 添加到上下文

CGContextAddPath(ctx, path.CGPath);

[[UIColor redColor] set];

// 渲染

CGContextFillPath(ctx);

// 第二个扇形

startA = endA;

angle =25/100.0* M_PI *2;

endA = startA + angle;

UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

[path1 addLineToPoint:center];

// 添加到上下文

CGContextAddPath(ctx, path1.CGPath);

[[UIColor greenColor] set];

// 渲染

CGContextFillPath(ctx);

// 第三个扇形

startA = endA;

angle =50/100.0* M_PI *2;

endA = startA + angle;

UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES];

[path2 addLineToPoint:center];

// 添加到上下文

CGContextAddPath(ctx, path2.CGPath);

[[UIColor blueColor] set];

// 渲染

CGContextFillPath(ctx);

}

第四种:柱形图

 - (void)drawRect:(CGRect)rect

{

NSArray *data = @[@25,@25,@50];

NSInteger count = data.count;

CGFloat w = rect.size.width / (2* count -1);

CGFloat h =0;

CGFloat x =0;

CGFloat y =0;

CGFloat viewH = rect.size.height;

// 1.获取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

for(inti =0; i < count; i++) {

h = viewH * [data[i] intValue] /100.0;

x =2* w * i;

y = viewH - h;

// 2.拼接路径

UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, w, h)];

// 3.添加路径到上下文

CGContextAddPath(ctx, path.CGPath);

[[UIColor randomColor] set];

// 4.渲染

CGContextFillPath(ctx);

}

}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{

[self setNeedsDisplay];

}

第五种:模仿UIImageView

 - (void)setImage:(UIImage *)image

{

_image = image;

[self setNeedsDisplay];

}

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect

{

// Drawing code

[_image drawInRect:rect];

}


第六种:图形上下文线

 - (void)drawRect:(CGRect)rect

{

// Drawing code

// 1.获取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 把ctx拷贝一份放在栈中

CGContextSaveGState(ctx);

// 2.拼接路径(绘图的信息)

UIBezierPath *path = [UIBezierPath bezierPath];

[path moveToPoint:CGPointMake(10,125)];

[path addLineToPoint:CGPointMake(240,125)];

// 3.路径添加到上下文

CGContextAddPath(ctx, path.CGPath);

// 设置绘图的状态

[[UIColor redColor] set];

CGContextSetLineWidth(ctx,10);

CGContextSetLineCap(ctx, kCGLineCapRound);

// 4.渲染

CGContextStrokePath(ctx);

// 第二根线

UIBezierPath *path1 = [UIBezierPath bezierPath];

[path1 moveToPoint:CGPointMake(125,10)];

[path1 addLineToPoint:CGPointMake(125,240)];

CGContextAddPath(ctx, path1.CGPath);

// 把栈顶上下文取出来,替换当前上下文

CGContextRestoreGState(ctx);

// 设置绘图的状态

//    [[UIColor blackColor] set];

//    CGContextSetLineWidth(ctx, 1);

//    CGContextSetLineCap(ctx, kCGLineCapButt);

// 4.渲染

CGContextStrokePath(ctx);

}

每日更新关注:https://weibo.com/hanjunqiang新浪微博

第七种:矩形操作

 - (void)drawRect:(CGRect)rect

{

// Drawing code

// 1.获取上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 注意:你的路径一定放在上下文矩阵操作之后

// 平移上下文

CGContextTranslateCTM(ctx,50,100);

// 旋转上下文

CGContextRotateCTM(ctx, M_PI_4);

// 缩放上下文

CGContextScaleCTM(ctx,0.5,1.2);

// 2.拼接路径

UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-50, -100,150,200)];

// 3.把路径添加到上下文

CGContextAddPath(ctx, path.CGPath);

[[UIColor redColor] set];

// 4.渲染

CGContextFillPath(ctx);

}

详细:https://www.2cto.com/kf/201608/542831.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • Quartz2D以及drawRect的重绘机制字数1487 阅读21 评论1 喜欢1一、什么是Quartz2D Q...
    PurpleWind阅读 760评论 0 3
  • 简述: 1、Quartz2D是什么Quartz2D是二维绘图引擎,同时支持IOS和Mac 2、Quartz2D能做...
    LitterL阅读 633评论 0 6
  • 什么是Quartz2D 是一个二维的绘图引擎,同时支持iOS和Mac系统 Quartz2D的API是纯C语言的,它...
    Mario_ZJ阅读 574评论 0 1
  • 新鲜的! 国庆回家,在XX汽车站前人山人海,在进站口排队一会儿后,看到进站口前有一条横幅“A, B, C, D, ...
    计划逃跑阅读 368评论 3 1
  • 你不必做什么 我接受 然后我就处于停滞的状态,讲真,要是没有别的事,我这样不死不活一辈子都可以。所以说有时候活着是...
    蹒跚幸福阅读 241评论 0 0