关于Core Animation
Core Animation is a graphics rendering and animation infrastructure available on both iOS and OS X that you use to animate the views and other visual elements of your app. With Core Animation, most of the work required to draw each frame of an animation is done for you. All you have to do is configure a few animation parameters (such as the start and end points) and tell Core Animation to start. Core Animation does the rest, handing most of the actual drawing work off to the onboard graphics hardware to accelerate the rendering. This automatic graphics acceleration results in high frame rates and smooth animations without burdening the CPU and slowing down your app.
CoreAnimation是一个在iOS和OS X均可使用的图形渲染和动画的基础框架,可以使用该框架为app中的视图和可视元素提供动画效果。借助CoreAnimation,大多数的帧动画已经由框架完成。你所需要做的只是设置动画的一些属性(如动画的起点和终点)和启动动画。其余部分由CoreAnimation完成,将大部分实际绘图工作交给板载图形硬件以加快渲染速度。这种自动图形加速功能可实现高帧率和流畅的动画效果,而不会给CPU造成负担并降低应用程序的运行速度。
简单点说,Core Animation 本质上可以理解为是一个复合引擎,不单单只负责动画,还包括渲染和构建。
iOS的渲染核心是CoreAnimation。从上到下依次是UIKit -> Core Animation -> (Metal / Core Graphics) -> Graphics Hardware 。
在apple发布metal框架以前,第三层是(OpenGL ES / Core Graphics) ,现在已经替换为(OpenGL ES / Core Graphics)。
在上图中,UIKit/AppKit所处的位置是在应用层,即application,主要是负责视图的布局和处理事件响应等工作,CoreAnimation、 (Metal / Core Graphics) 和Graphics Hardware所处的位置,是iOS视图的渲染和动画所处的另一个进程完成,这个进程称为RenderServer。
CALayer
本质上而言,CALayer 是用户所能在屏幕上看见的一切的基础。UIKit之所以能显示可视化内容是因为每一个UIIKit的UI控件内部都关联一个CALayer,即backing layer。
那么为什么 CALayer 可以呈现可视化内容呢?因为 CALayer 基本等同于一个 纹理。纹理是 GPU 进行图像渲染的重要依据。
纹理本质上就是一张图片,因此 CALayer 也包含一个 contents 属性指向一块缓存区,称为 backing store,可以存放位图(Bitmap)。iOS 中将该缓存区保存的图片称为 寄宿图。
图形渲染流水线支持从顶点开始进行绘制(在流水线中,顶点会被处理生成纹理),也支持直接使用纹理(图片)进行渲染。相应地,在实际开发中,绘制界面也有两种方式:一种是 手动绘制;另一种是 使用图片。
对此,iOS 中也有两种相应的实现方式:
使用图片:contents image
手动绘制:custom drawing
Contents Image
Contents Image 是指通过 CALayer 的 contents 属性来配置图片。contents 属性的类型为 Any,因为在UIKit(iOS)中contents是CGImage,在AppKit()中contents是NSImage。
//CALayer
var contents: Any? { get set }
## Discussion
The default value of this property is `nil`.
If you are using the layer to display a static image, you can set this property to
the CGImage containing the image you want to display.
(In macOS 10.6 and later, you can also set the property to an NSImage object.)
本质上,contents 属性指向的一块缓存区域,称为 backing store,可以存放 bitmap 数据。
在CALayer的contents 属性官方文档介绍中有这样一段话:
If the layer object is tied to a view object, you should avoid setting the contents of this property directly. The interplay between views and layers usually results in the view replacing the contents of this property during a subsequent update.
也就是说,一般情况下layer总是被view持有,保持一一对应的关系,如果layer不是单独增加进layer tree,应避免直接设置contents属性。
Custom Drawing
Custom Drawing 是指使用 Core Graphics 直接绘制寄宿图。实际开发中,一般通过继承 UIView 并实现 -drawRect: 方法来自定义绘制。
虽然 -drawRect: 是一个 UIView 方法,但事实上都是底层的 CALayer 完成了重绘工作并保存了产生的图片。
UIView和CALayer的关系
前面说过渲染图形是由CALayer完成,那为什么还需要UIView呢?其原因在于要做 职责分离。
UIView的主要职责是创建并管理图层、布局和子View的管理以及处理事件响应(如屏幕点击事件),CALayer的主要职责是管理用户提供的可视化内容,即渲染和动画。
A layer’s main job is to manage the visual content that you provide but the layer itself has visual attributes that can be set, such as a background color, border, and shadow.
layers make it easier and more efficient to draw and animate the contents of views and maintain high frame rates while doing so. However, there are many things that layers do not do. Layers do not handle events, draw content, participate in the responder chain, or do many other things
职责分离能避免很多重复代码。在 iOS 和 Mac OS X 两个平台上,事件和用户交互有很多地方的不同,基于多点触控的用户界面和基于鼠标键盘的交互有着本质的区别,这就是为什么 iOS 有 UIKit 和 UIView,对应 Mac OS X 有 AppKit 和 NSView 的原因。它们在功能上很相似,但是在实现上有着显著的区别。
其实不单单只有视图树和图层树,还有表示树和渲染树。
Core Animation 流水线
了解UIView和CALayer的区别以及CALayer本质后,下面介绍Core Animation 流水线的工作原理。
上面说过,app本身并不负责渲染,仅仅是布局、管理视图层以及处理事件(HandleEvent),真正的渲染工作是在一个叫Render Server进程中完成的。
app通过IPC将渲染任务和相关数据提交给render server,render server处理完数据后,再传递给GPU,最后由GPU调用iOS的图像硬件(Graphics Hardware)进行显示。
Core Animation 流水线的详细过程如下:
- 由 app 处理事件(Handle Events),如:用户的点击操作,在此过程中 app 可能需要更新 视图树,相应地,图层树 也会被更新。
- app 通过 CPU 完成对显示内容的计算,如:视图的创建、布局计算、图片解码、文本绘制等。在完成对显示内容的计算之后,app 对图层进行打包,并在下一次 RunLoop 时将其发送至 Render Server,即完成了一次 Commit Transaction 操作。
- Render Server 主要执行 Open GL、Core Graphics 相关程序,并调用 GPU
- GPU 则在物理层上完成了对图像的渲染
- GPU 通过 Frame Buffer、视频控制器等相关部件,将图像显示在屏幕上
Commit Transaction
在 Core Animation 流水线中,app 调用 Render Server 前的最后一步 Commit Transaction 其实可以细分为 4 个步骤:
- Layout
- Display
- Prepare
-
Commit
Commit Transaction
Layout
Layout 阶段主要进行视图构建,包括:LayoutSubviews 方法的重载,addSubview: 方法填充子视图等
Display
Display
阶段主要进行视图绘制,这里仅仅是设置最要成像的图元数据。重载视图的 drawRect:
方法可以自定义 UIView
的显示,其原理是在 drawRect:
方法内部绘制寄宿图,该过程使用 CPU 和内存。
Prepare
Prepare
阶段属于附加步骤,一般处理图像的解码和转换等操作。
Commit
Commit
阶段主要将图层进行打包,并将它们发送至 Render Server
。该过程会递归执行,因为图层和视图都是以树形结构存在。