iOS端K线系列之绘制方法比较(CoreGraphics、CoreAnimation)

写在前面

关于在IOS端进行原生界面绘制,苹果开发文档里明确提供了几种方法:

  1. 使用系统提供的标准视图,例如lists, collections, alerts, images, progress bars, tables等。
  2. 使用Core Animation的图层,Core Animation不仅提供了动画的类,还提供了显示内容的图层类。
  3. 使用OpenGL ES,这个框架提供了一套开放标准的图形绘制库,主要面向游戏开发或者需要高帧速率的app。
  4. 使用UIWebView类展示基于web的图形界面。

很显然,如果你要开发一套K线框架:

  • 第一种方法肯定不适合,因为你没办法去用标准的控件来显示K线;
  • 第四种使用webview,这样的话就需要使用百度的echarts,或者还可以使用highcharts
  • 所以,想要开发原生K线,就只能选择第二种和第三种方法了
  • OpenGL ES框架使用起来比较麻烦,后续会单独在一篇文章中介绍如何使用
  • 所以,在这里,就只介绍第二种方法,也就是使用Core Animation
  • 其实,还有一种办法,就是绕过Core Animation,直接使用Core Graphics进行绘制。这个在后面会说到。

Core Animation是什么?

在苹果的开发文档中,有关于Core Animation的介绍,点击这儿

这里放一张非常经典的图:


Core Animation是一个图形渲染和动画的基础库,是一个复合引擎,职责就是尽可能快地组合屏幕上不同的可视内容,这个内容是被分解成独立的图层,存储在一个叫图层树的体系之中。
Core Animation可以直接用在Max OS X和IOS平台上。
Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程。
Core Animation是直接作用于CALayer,并非直接作用于UIView。
Core Animation有以下几个分类:

  • 提供显示内容的图层类
  • 动画和计时类
  • 布局和约束类
  • 事务类,在原子更新的时候组合图层类

CoreAnimation使用

在绘制K线时,主要是使用CALayer的子类CAShapeLayer,它是一个通过矢量图形而不是bitmap来绘制的图层子类。使用时,可以直接指定诸如颜色和线宽等属性,用CGPath来定义线稿绘制的图形,最后CAShapeLayer就自动渲染出来了。
首先先从最简单的开始,画一条线,代码如下:

    //初始化一个线的图层
    CAShapeLayer *lineLayer = [CAShapeLayer layer];
    //初始化一个描述的路径
    UIBezierPath *linePath = [UIBezierPath bezierPath];
    //设置线段开始的点
    [linePath moveToPoint:beginPoint];
    //设置线段结束的点
    //这里也可以添加多个点
    [linePath addLineToPoint:endPoint];
    //设置图层路径
    lineLayer.path = linePath.CGPath;
    //设置图层的其他属性
    lineLayer.lineWidth = lineWidth;
    lineLayer.strokeColor = lineColor.CGColor;
    lineLayer.fillColor = [UIColor clearColor].CGColor;

执行结果如下:


从代码上可以看到,绘制线段的步骤很简单:

  1. 初始化一个图层
  2. 初始化用户线段的路径
  3. 添加线段开始的坐标点
  4. 再添加多个中间的坐标点
  5. 最后添加结束的坐标点
  6. 把路径设置到图层上去
  7. 设置图层的各个属性

其实现在再想一想K线中的分时线(如果不了解,可以点击这儿),可以直接用这段代码来绘制出来,当然,不包括分时线下方的背景颜色,并且得添加多个坐标点。
画完一条线后,再来画一个方块:

    //初始化一个rect
    CGRect frameRect = CGRectMake(x, y, width, height);
    //初始化一个图层
    CAShapeLayer *layer = [CAShapeLayer layer];
    //初始化一个描述框的路径
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:frameRect];
    //把路径设置到图层中
    layer.path = path.CGPath;
    //设置图层的各个属性
    layer.strokeColor = strokeColor.CGColor;
    layer.fillColor = backColor.CGColor;

执行结果如下:


绘制的步骤也很简单:

  1. 设置好框的frame
  2. 然后初始化路径的时候,直接把框的frame赋值进去
  3. 初始化一个图层
  4. 把路径设置到图层中
  5. 设置图层的各个属性

在这里,如果留心一下,其实就可以想到,框已经绘制好了,那一个蜡烛也就绘制好了,那绘制多个框,一整屏的蜡烛图不就绘制出来了。(没留心的,可以点这儿)如果你已经理解了上面所说的,那么我们在K线框架开发的道路上已经走出一大步。

Core Graphics的使用

上面有第一个地方说到,绘制界面除了使用Core Animation以外,还可以绕过Core Animation直接使用OpenGL ES或者Core Graphics。

在这里,介绍一下Core Graphics。Core Graphics是一套基于C的API框架,使用了Quartz作为绘图引擎。它提供了低级别、轻量级、高保真度的2D渲染。该框架可以用于基于路径的绘图、变换、颜色管理、脱屏渲染,模板、渐变、遮蔽、图像数据管理、图像的创建、遮罩以及PDF文档的创建、显示和分析。
和上面一样,也是从最基础的一条线开始:

    //获取当前上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    //开始记录路径
    CGContextBeginPath(ctx);
    //设置开始坐标点
    CGContextMoveToPoint(ctx, beginPoint);
    //添加坐标点,或者也可以只添加一个坐标点
    CGContextAddLineToPoint(ctx, endPoint);
    //结束记录路径
    CGContextClosePath(ctx);
    //设置线段宽度
    CGContextSetLineWidth(ctx, lineWidth);
    //设置颜色
    CGContextSetStrokeColorWithColor(ctx, lineColor.CGColor);
    //开始绘制路径
    CGContextStrokePath(ctx);

执行效果和上面使用CAShapeLayer绘制线段的效果一样。

依次类推,可以绘制一条线,也就可以绘制一个框。

这里说明一下,代码中的CGContext 上下文定义了绘制的地方。在使用UIKit时,上下文是唯一的,UIKit会维护着一个上下文堆栈,而UIKit方法总是绘制到最顶层的上下文中。

一般使用Core Graphics进行绘制,都会重写drawRect方法,所以这里的上下文就是上下文堆栈最顶层的上下文,使用UIGraphicsGetCurrentContext就可以获取到。

两者比较

这里总结一下,上述介绍了两种绘制图形的方式:

  1. 使用Core Animation的 CAShapeLayer图层子类
  2. 使用Core Graphics

那两种方式有什么区别呢?

1. CAShapeLayer渲染更快速。因为它使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
2. CAShapeLayer更高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。
3. CAShapeLayer不会被图层边界剪裁掉,一个 CAShapeLayer 可以在边界之外绘制。你的图层路径不会像在使用 Core Graphics 的普通 CALayer 一样被剪裁掉。
4. CAShapeLayer不会出现像素化。当你给 CAShapeLayer 做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。

其实最后一点最关键,因为它正好符合了K线框架开发的业务需求。

一旦你实现了 CALayerDelegate 协议中的 -drawLayer:inContext: 方法或者 UIView 中的 -drawRect: 方法(其实就是前者的包装方法),图层就创建了一个绘制上下文,这个上下文需要的内存可从这个公式得出:图层宽x图层高x4字节,宽高的单位均为像素。对于一个在 Retina iPad 上的全屏图层来说,这个内存量就是 2048x1526x4字节,相当于12MB内存,图层每次重绘的时候都需要重新抹掉内存然后重新分配。

而当我们使用K线的时候,左滑或者右滑时,都会触发重新绘制,而每次重绘时都会重新获取一个绘制上下文。而左滑或者右滑时,会高频率的进行重绘,所以避免不了内存的重新分配。

当然,这里也不是说Core Graphics效率就很差,只是恰好在这样的业务需求下,会把某一个问题放大。并且,不要忘了CAShapeLayer绘制的图形是直接操作layer,不会作用于UIView,更不会去响应用户的交互。

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

推荐阅读更多精彩内容