-
通过一副流程图看UI视图的绘制步骤:
当调用[UIView setNeedsDisplay]之后,实际上并没有立马发生当前视图的绘制工作,而是在之后的某一时机才会进行当前UI视图的真正绘制工作。
当调用[UIView setNeedsDisplay],系统会立马调用layer的同名方法setNeedsDisplay,之后相当于在当前layer上打上了一个脏标记,然后会在当前runloop将要结束的时候才会调用[CALayer display],然后进入到当前视图的真正绘制流程当中。
[CALayer display]的内部实现当中首先会判断这个layer的delegate是否会响应displayerLayer方法,假如不响应就会进入到系统的绘制流程当中。如果响应的话,实际上就提供了异步绘制的入口,或者说给我们进行异步绘制留有余地,这就构成了UIView绘制原理的过程。
-
接下来看一下系统的绘制流程:
在drawRectangle:方法当中,可以通过上下文堆栈当中取出栈顶的context,拿到的就是当前控件或者视图的上下文或者是backingStore,然后layer会判断它是否有代理,如果没有的话,会掉用drawInContext:,如果有的话,会调用layer的代理方法drawLayerInContext,然后做当前视图的绘制工作,这一部分是发生在系统的内部当中的 ,然后在一个合适的时机给与我们一个回调方法,也就是[UIView drawRect:]方法,这个方法默认实现是什么都不做,而给我们开这个口子,是允许在系统绘制的基础之上再做一些其他的相关绘制工作。
不论是哪种分支,之中都是由CALayer上传到对应backingStore到GPU,这里的backingStore可以理解为最终的位图,之后就结束了系统默认的绘制流程。
-
怎么进行异步绘制?
实际上就是基于系统开的口子,layer的delegate,如果遵从了displayLayer方法,或者说实现了这个方法的话,我们就可以进入到异步绘制的流程当中,在异步绘制当中就需要代理去负责生成对应的bitmap,同时需要我们把位图作为layer的content属性提交到layer的content属性当中。
通过下图了解异步绘制的机制和流程:
左侧是一个主队列,右侧是一个全局并发队列,假如在某一时机调用了setNeedsDisplay,在当前runloop将要结束的时候会由系统调用视图所对应layer的display方法,然后如果代理实现了displayLayer函数的时候,会调用代理的displayerLayer函数方法,然后会通过子线程的切换,在子线程当中去做位图的绘制,此时主线程可以做一些其他的工作。
在全局并发队列子线程当中所做的工作主要有这么几个步骤,第一个是通过CGBitmapContextCreate() 来创建位图的上下文,然后通过CoreGraphic的相关API做当前UI的一些绘制工作,之后我们再通过CoreGraphic的相关函数CGBitmapContextCreateImage来根据当前所绘制的上下文生成一张CGImage图片,再回到主队列当中,提交这个位图,设置给 CALayer 的content属性,这样的话就完成了一个UI控件的异步绘制过程。