@qidiandasheng
2016-09-17T15:21:56.000000Z
字数 3455
阅读 4910
iOS界面开发
下面两张图表示UIView
和CALayer
所属的框架和继承体系
详解CALayer 和 UIView的区别和联系这篇文章中有对这两个概念的一个详细理解。
我这里简单概括一下主旨:
UIView是继承自UIResponder的,所以说UIView是可以响应事件的,而CALayer是不能的。
也就是说UIView负责处理用户交互,负责绘制内容的则是它持有的那个CALayer,我们访问和设置UIView的这些负责显示的属性实际上访问和设置的都是这个CALayer对应的属性,UIView只是将这些操作封装起来了而已。
一个UIView只有一个相关联的CALayer(自动创建),同时它也可以支持添加无数多个子CALayer([View.layer addSublayer:layer];
),每个CALayer显示一种东西,增强UIView的展现能力。
用途
使用UIView(视图)
的好处在于,你能在使用所有CALayer底层特性的同时,也可以使用UIView的高级API(比如自动排版,布局和事件处理)。
而使用CALayer(图层)
的情况主要有:
1:开发同时可以在Mac OS上运行的跨平台应用。
2:使用多种CALayer的子类,并且不想创建额外的UIView去包封装它们所有
3:做一些对性能特别挑剔的工作,比如对UIView一些可忽略不计的操作都会引起显著的不同。
所以说CALayer
比UIView
更轻量级,如果不需要一些用户交互的话可以直接使用CALayer
绘制,这样对于性能有一定的优化。而UIView
当然使用起来更方便。
我们上文提到,CALayer
是负责绘制内容管理的一个类,而真正的绘制部分是由CoreGraphics Framework
框架来处理的。CoreGraphics
中包括下图所示的各种类来处理绘制,比如path的绘图工作(如,CGPath)、变形操作(如,CGAffineTransform)、颜色管理(如,CGColor)、离屏渲染(如,CGBitmapContextCreateImage)、渲染模式(patterns)、渐变(gradients)、阴影效果、图形数据管理、图形创建、蒙版以及PDF文档的创建、显示和解析等等。
而
UIBezierPath
继承自NSObject
,是UIKit
下关于CoreGraphics
中的路径绘制部分CGPath
的封装。
使用UIBezierPath
可以创建基于矢量的路径,如果是基于矢量形状的路径,都用直线和曲线去创建。我们使用直线段去创建矩形和多边形,使用曲线去创建圆弧(arc)、圆或者其他复杂的曲线形状。
关于整个界面渲染的流程这篇文章中有较好的描述。
UIKit Framework它主要提供了:界面呈现能力、事件响应能力、驱动RunLoop运行和与系统内核通信的数据。简单来说就是:主要负责界面展示、事件响应以及是RunLoop的需求方。UIView当然是属于UIKit Framework。
QuartzCore Framework 与 CoreAnimation它提供了图形处理和视频图像处理的能力。简单来说就是:QuartzCore Framework负责把图形图像最终显示到屏幕上,这里我觉得应该是特指CoreAnimation。不要从字面上去理解CoreAnimation的职责,因为它的核心工作不单是负责动画的创建和执行,它还负责把我们用代码构建的界面显示到屏幕上,实际上是CoreAnimation通过OpenGLES做的。CALayer是属于QuarzCore Framework下的CoreAnimation。
CoreGraphics Framework一个基于C库函数的高级绘画引擎,它提供了非常强大的轻量级2D渲染能力。可以使用CoreGraphics处理基于path的绘图工作(如,CGPath)、变形操作(如,CGAffineTransform)、颜色管理(如,CGColor)、离屏渲染(如,CGBitmapContextCreateImage)、渲染模式(patterns)、渐变(gradients)、阴影效果、图形数据管理、图形创建、蒙版以及PDF文档的创建、显示和解析。 千万别和QuartzCore混淆,CoreGraphics负责创建显示到屏幕上的数据模型,QuartzCore(CoreAnimation –> OpenGLES)负责把CoreGraphics创建的数据模型真正显示到屏幕上。 CG打头的类都是属于CoreGraphics Framework
以上三者的关系 三者的关系是通过界面展示以及动画的创建、执行关联起来的,所以它们之间是协作而不是从属的关系。
整个绘制流程主要就是表现在几个框架之间的协作关系,UIKit Framework
、QuartzCore Framework(CoreAnimation)
、CoreGraphics Framework
。整个协作流程如下图所示:
下面按照步骤来讲解:
1:触发界面渲染的情况
1.1 通过在loadView过程中debug子view的drawRect:方法得知:RunLoop处于kCFRunLoopBeforeWaiting状态时会回调CoreAnimation中监听kCFRunLoopBeforeWaiting状态的RunLoopObserver,从而通过RunLoopObserver来进一步调用CoreAnimation内部的CA::Transaction::commit() ();方法,进而一步一步地调用到drawRect方法。
1.2 通过在VC里给一个按钮添加点击事件,并在事件对应的selector中修改子view的背景色,debug子view的drawRect:方法得知:RunLoop被iOS系统传递来的点击事件唤醒并由source1处理(__IOHIDEventSystemClientQueueCallback),并且在下一个runloop里由source0转发给UIApplication(_UIApplicationHandleEventQueue),从而能过source0里的事件队列来调用CoreAnimation内部的CA::Transaction::commit() ();方法,进而一步一步的调用drawRect方法。
上面两种情况都是触发CoreAnimation的CA::Transaction::commit() ();方法来达到触发CALayer/UIView的渲染,所以这个CA::Transaction机制很关键。
2:其实这一步已经进入到了Quarz Core的内部(Core Animation),即调用CA::Transaction::commit() ();
来创建CATrasaction
,然后进一步调用-[CALayer drawInContext:] ()
3:回调CALayer
的Delegate(UIView)
,问UIView
没有需要画的内容,即回调到drawRect:
方法。
4:在drawRect:
方法里可以通过CoreGraphics
函数或UIKit
中对CoreGraphics
封装的方法(如 UIBezierPath)进行画图操作,这些画图的操作内容都是以Off-Screen离屏(广义的离屏,因为没有在GPU中进行)方式进行画图。另,注意图中虚线部分的3|4步骤的情况:因为CALayer可以单独存在进行界面渲染,所以CALayer也可以直接与CoreGraphics产生联系。
5:无论是有UIView参与的或是直接采用CALayer渲染的操作都会体现在CALayer上(在没有CoreGraphics参与的情况下,UIView或CALayer本身也有一些在业务层面需要显示的内容,所以这里说的“体现在CALayer上”,是泛指UIViewr的子视图或CALaye的子图层以及CoreGraphics参与的内容)。
6:CoreAnimation(CALayer)把它的内容转成位图(纹理),然后通过OpenGLES把位图内容传送到GPU的帧缓冲区。
7:等到由iOS显示屏时钟信号驱动的VSync信号来临时,则把GPU帧缓冲区里的内容显示到iOS显示屏上。