关于渲染机制是很多开发人员容易忽略掉的,很多时候认为会用就可以了,其实知道底层渲染机制特别是渲染的时间点对我们是很有帮助的。
这里有篇文章写的不错:https://blog.csdn.net/yangyangzhang1990/article/details/52452707/
本文对该文章结合自己的经验进行结论整理。
绘制流程
当在操作 UI 时,比如改变了 Frame、更新了 UIView/CALayer 的层次时,或者手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,这个 UIView/CALayer 就被标记为待处理,并被提交到一个全局的容器去。
苹果注册了一个 Observer 监听 BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) 事件,回调去执行一个很长的函数:_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。这个函数里会遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。
drawRect什么时候会被调用?
前提:如果UIView没有设置rect大小,将直接导致drawRect不被调用。
1. setNeedsDisplay:需要重绘,异步调用drawRect
2. - (void)setNeedsDisplayInRect:(CGRect)rect:需要局部重绘,同样会异步调用drawRect
3. 通过设置view的contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect。
4. 调用sizeThatFits会触发调用drawRect。
drawRect性能优化原则:缩小绘制区域,降低重绘频率
layoutSubviews什么时候会被调用?
前提:如果UIView没有设置rect大小,将直接导致layoutSubviews不被调用。
1.调用setNeedsLayout 或者 layoutIfNeeded的时候
2.addSubview的时候。
3.当view的size发生改变的时候。
4.滑动UIScrollView的时候。
5.旋转Screen会触发父UIView上的layoutSubviews事件。
这里顺便说明下setNeedsLayout和layoutIfNeeded区别
1. setNeedsLayout
标记为需要重新布局,不立即刷新,但layoutSubviews一定会被调用,配合layoutIfNeeded立即更新
2. layoutIfNeeded
如果有需要刷新的标记,立即调用layoutSubviews进行布局
3. 用法
如果要立即刷新,要先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现布局。
事实上我们主动调用这2个方法的时候不多,大多数情况是通过约束初始化控件后,要及时得到控件的大小的时候,主动调用下layoutIfNeeded。