iOS UI绘制

作者: Summit_yp | 来源:发表于2020-05-10 20:04 被阅读0次

一、UIView的绘制流程

UIView是如何到显示的屏幕上的。

这件事要从RunLoop开始,RunLoop是一个60fps的回调,也就是说每16.7ms绘制一次屏幕,也就是我们需要在这个时间内完成view的缓冲区创建,view内容的绘制这些是CPU的工作;然后把缓冲区交给GPU渲染,这里包括了多个View的拼接(Compositing),纹理的渲染(Texture)等等,最后Display到屏幕上。但是如果你在16.7ms内做的事情太多,导致CPU,GPU无法在指定时间内完成指定的工作,那么就会出现卡顿现象,也就是丢帧。

1.当调用UIView的setNeedsDisplay后系统会立刻调用view的layer的同名方法[view.layer setNeedsDisplay],之后相当于在layer上面打上了一个脏标记

2.然后再当前runloop将要结束的时候,才会调用CALayer的display函数方法,然后才进入到当前视图的真正绘制工作的流程当中

3.CALayer的display方法,在内部会首先判断layer的delegate是否响应displayLayer这个方法

若不响应,则系统开始绘制流程     

若响应,则开始异步绘制 (主要讲系统绘制流程,异步后续学习后会补充)

二、系统绘制流程

1.首先CALayer内部会创建一个CGContextRef,在drawRect方法中,可以通过上下文堆栈当中的取出这个context,拿到的就是当前视图的上下文或者说是backing store

- (void)drawRect:(CGRect)rect{

    //获取上下文

    CGContextRef ctx = UIGraphicsGetCurrentContext();

}

2.判断layer是否有delegate:

    2.1如果有delegate,则会执行[layer.delegate drawLayer:inContext](这个方法的执行是在系统内部执行的),然后在这个方法中会调用view的drawRect:方法,也就是我们重写view的drawRect:方法才会被调用到。

    2.2如果没有delegate,会调用layer的drawInContext方法,也就是我们可以重写的layer的该方法,此刻会被调用到。

3.最后把绘制完的backing store(可以理解为位图)提交给GPU。

什么情况会调用draw rect方法

1、controller的loadView、viewdidLoad方法调用之后,view即将出现在屏幕之前系统调用drawRect。

2、sizeToFit方法调用之后。

3、设置contetMode为UIViewCOntentModelRedraw,之后每次更改frame的时候调用redraw方法。

4、调用setNeedsDisplay方法。

就以上流程来说,我们开发时一般也只是重写drawRect方法来实现自定义视图,而在阅读代码后,发现航班动态并未重写drawRect方法更别说再其中进行耗时操作了。那么到这好像并没有发现航班动态卡顿的终极原因。继续查找资料发现,以上的drawRect,setNeedsDisplay等方法都是绘制方法,与之相关的还有布局方法。

视图布局相关方法:

1、-(void)layoutSubviews;

对subview重新布局

2、-(void)setNeedsLayout;

将视图标记为需要重新布局,这个方法会在系统runloop的下一个周期自动调用layoutSubviews。

3、-(void)layoutIfNeeded;

如果有需要刷新的标记,立即调用layoutSubviews进行布局

    在没有外界干预的情况下,一个view的frame或者bounds发生变化时,系统会先去标记flag这个view,等下一次渲染时机到来时(也就是runloop的下一次循环),会去按照最新的布局去重新布局视图。

setNeedLayout就是给这个view添加一个标记,告诉系统下一次渲染时机需要重新布局这个视图。

layoutIfNeed就是告诉系统,如果已经设置了flag,那不用等待下个渲染时机到来,立即重新渲染。前提是设置了flag。

    而layoutSubviews则是由系统去调用,不需要我们主动调用,我们只需要调用layoutIfNeed,告诉系统是否立即执行重新布局的操作。

layoutSubviews调用时机

1、init初始化不会触发layoutSubviews。

2、addSubview会触发layoutSubviews。

3、设置view的Frame会触发layoutSubviews,(前提是frame的值设置前后发生了变化。)

4、滚动一个UIScrollView会触发layoutSubviews。

5、旋转屏幕会触发父UIView上的layoutSubviews事件。

6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。

7、直接调用setLayoutSubviews。(不建议这么做)

   其次在学习过程中还了解到viewController的loadView方法的一些原理 。loadView方法负责创建UIViewController的view,每次访问UIViewController的view,比如controller.view、self.view,且view为nil,就会调用loadView方法。

    关于[super loadView]中的默认实现:(1)首先去查找与UIViewController相关联的xib文件,通过加载xib文件来创建UIViewController的view;(2)如果没有找到关联的xib文件,就会创建一个空白的UIView,然后赋值给UIViewController的view属性。

    在项目中大量viewController都重写了loadView方法,并在其中创建view并赋值给self.view。

   重点在红框中,大致的意思是我们可以在此方法中直接创建view并赋值给viewController的view属性,但如果我们这么做了,就不用再调用super方法了。这样能节省一点开销。

相关文章

  • iOS UI绘制

    一、UIView的绘制流程 UIView是如何到显示的屏幕上的。 这件事要从RunLoop开始,RunLoop是一...

  • iOS UI绘制原理

    iOS UI绘制原理 高质量的图形展示在app的交互界面中扮演非常重要的角色。高质量的图形展示让用户更能喜欢使用它...

  • iOS UI绘制原理

    一、UIView的绘制流程 下面我们来对这个流程进行说明: 当我们调用[UIView setNeedsDispla...

  • Flutter的渲染机制

    Flutter能够做到跨平台是因为使用的是Skia渲染引擎来绘制UI,不仅可以保证Android和iOS上UI的一...

  • Cocoa Day01 View相关

    1. 现在 UI 一般用 Sketch 绘图,当 UI 要开发绘制阴影时,会给出如下阴影数值,它们分别对应 iOS...

  • AsyncDisplayKit

    AsyncDisplayKit是Facebook开源的一套用于iOS界面流畅的异步绘制UI的框架 Runloop ...

  • Android性能优化

    1.UI绘制 减少UI绘制时间;减少不必要的子控件或层级;UI的绘制流程:measure-layout-draw,...

  • UI绘制-Paint(一)Paint基本属性及方法

    系列文章传送门:UI绘制-Paint(一)Paint基本属性及方法UI绘制-Paint(二)颜色相关方法UI绘制-...

  • UI绘制-Paint(二)颜色相关方法

    系列文章传送门:UI绘制-Paint(一)Paint基本属性及方法UI绘制-Paint(二)颜色相关方法UI绘制-...

  • UI绘制-Paint(三)图层混合模式

    系列文章传送门:UI绘制-Paint(一)Paint基本属性及方法UI绘制-Paint(二)颜色相关方法UI绘制-...

网友评论

    本文标题:iOS UI绘制

    本文链接:https://www.haomeiwen.com/subject/tarjnhtx.html