iOS 关于View的一切

iOS 关于View的一切

app展示给用户的页面,一般都是由windows和Views来展现的,windows基本上没有视觉上可以展示的内容,它是为views提供了一个容器,来承载views,而view是UIView的实例,它管理着window中的一块长方形区域,可以用来展示图片、文字、按钮以及它们的组合等等。

1、View

view对象是UIView类的实例,用来展示可视化的内容。视图对象定义了一个矩形区域,并且负责该区域内容的绘制、事件的处理及子视图的布局。每个view对象都对应一个layer对象,layer对象是CALayer的实例,该对象管理着视图的后台存储、处理与视图相关的动画等等。一般通过视图的layer属性可以访问view对应的layer对象。

使用Core Animation layer对象对性能有影响。一个View的绘制代码调用后,为了之后的重用,Core Animation缓存了绘制的内容。重用绘制内容减少视图更新时需要的绘图周期,提高部分性能。

2、View作用
  • views负责绘制显示的内容:使用Core Graphics、OpenGL ES或者UIKit在管理的长方形区域内绘制形状、图片或者文字

  • 处理多点触摸事件:使用gesture recognizers来响应触摸事件或者直接处理触摸事件

  • 管理子视图的布局:负责**动态(如果界面旋转,子视图可以动态适应这些变化,优雅的展示)**的对子视图布局,包括对子视图位置及大小的设置。

    避免所有的展示内容在同一个view中,这样代码难以维护,可以将展示的内容划分成不同的板块,每一个板块由一个视图块构成,每个视图块可以有自己的视图层次。

3、View层级

一个视图加在另一个视图上面,那么这两个视图就构成了父视图与子视图的关系,子视图可以覆盖父视图的一部分也可以完全覆盖父视图,父视图按照子视图的添加顺序,将子视图对象存放在数组中,这样就构成了视图层级,父视图按照子视图加入的顺序来管理子视图。视图层级影响子视图的可见性及对事件的响应。

  • 影响子视图的可见性

    如果A、B两个视图先后加入父视图中,如果A、B两个视图的位置相同,那么B视图将遮盖A视图,在层级上,B视图在A视图的上面。

  • 影响视图的行为

    改变父视图的尺寸、隐藏父视图、改变父视图的透明度以及对父视图的坐标系统做数据变换都将影响子视图的行为;例如改变父视图的尺寸,子视图的大小以及子视图在父视图中的位置都将变化,所以应该配置视图的相应的属性,当父视图尺寸变化时,可以控制子视图的尺寸来适应父视图的变化。

  • 影响对事件的响应

    视图层级影响视图对事件的响应。当用户触摸某个视图(例如触摸的视图是A视图)后,系统直接给A视图发送一个事件对象以及触摸信息,让A视图处理增额触摸事件。 但是,如果A视图选择不处理这个触摸事件,那么它可以把这个触摸事件传递给它的父视图,让它的父视图来处理这个触摸事件; 如果它的父视图也没有处理该事件,则把该事件传递给父视图的父视图,以此类推。如果没有对象处理该事件,那么这个事件最终会被传递给应用程序对象,通常由应用程序对象把它丢弃。

4、视图的绘制

(1)视图绘制过程

View 实现了按需绘制自己展示的内容。当视图第一次展示自己的内容时, view 根据系统的要求绘制自己要展示的内容;之后,系统捕捉 view 绘制内容并且生成快照,系统使用这个快照来作为 view 的视觉呈现,展现出来。如果之后未对该视图进行改变,那么该视图的绘制代码将不再执行,但是如果之后改变了这个视图,需要调用setNeedsDisplay 或者 setNeedsDisplayInRect 通知系统视图已经被修改,当前的快照无效,需要在下一个runloop 进行重新绘制。

系统对 view 的重绘是在下一个 runloop 中进行的,所以在当前runloop到下一个runloop开始有一段时间,在这个时间中可以操作视图层级,比如隐藏、移除某个视图或者调整视图的位置、大小等等。这些修改都将在下一个 runloop 中一起呈现。

(2)重新绘制view

  • 系统视图:系统使用私有方法重新绘制view,并且提供可以配置的属性或者其他接口供我们配置系统视图的外观,我们不需要关心系统视图的绘制。

  • 自定义视图:一般都是在自定义view的类中重载 drawRect:方法重新绘制 view 。当某些情况下,如果想要重画 View ,一般 setNeedsDisplay 方法,而不是强行调用 drawRect 方法,强行调用 drawRect 方法是无效果的。

    注意

    drawRect 的调用时机

    • 对于 ViewController,系统在 ViewController 执行 loadView、ViewDidLoad 之后自动调用 drawRect: 方法;对于 UIView,系统在 layoutSubviews 调用之后会调用 drawRect: 方法。

    • 调用 sizeToFit 之后,系统自动调用 drawRect 方法。(因为调用 sizeThatFits 后, 控件的 frame值将改变, UIView 的 layoutSubviews 会被调用, 系统在调用然后再调用 layoutSubviews 方法之后, 调用 drawRect 方法)。

    • 设置 View 的 contentMode 值为 UIViewContentModeRedraw。那么将在每次设置或更改 bounds 或者 frame 时,自动调用 drawRect。

    • view 的 rect 不为0的情况下,直接调用 setNeedsDisplay 或者 setNeedsDisplayInRect: 触发 drawRect: 方法。

    drawRect方法使用注意点

    • 使用UIView绘图,只能在 drawRect:方法中获取相应的 contextRef 并绘图。需要调用 setNeedDisplay 等来触发 drawRect:方法。
    • 使用calayer绘图,只能在drawInContext: 中或在 delegate 的相应方法中绘制,需要调用 setNeedDisplay 等来触发。
    • 在实时画图时, 如果使用 gestureRecognizer 来获取 point 的坐标;,使用 touchbegan 等方法调用 setNeedsDisplay 实时刷新屏幕。

(3)触发view重新绘制的操作

  • 操作某个视图时,例如 view 的 frame 变化 或者 view 的层次发生变化时。(操作 View 后,系统调用 layoutSubviews,自动调用 drawRect:)

  • 使view从隐藏状态变为可见时,例如设置某个视图的hidden属性为NO

  • 滚出屏幕的视图,重新在屏幕上展示时

  • 显示调用视图的setNeedsDisplay或者setNeedsDisplayInRect方法。(最好不要直接调用drawRect)

    setNeedsDisplay是重新绘制整个视图,setNeedsDisplayInRect是重新绘制视图的部分区域

在执行以上操作后,view 将被标记为重绘,并在下一个 runloop 中,如果是系统视图,将调用私有方法重新绘制视图;如果是自定义视图,将调用 drawRect: 重新绘制。

5、运行时视图的交互模式

应用在运行过程中,用户点击应用界面的某个按钮,应用需要及时响应用户的点击。比如,微博app中,用户点击某条微博,应用给出的响应就是展示这条微博的纤细内容;从用户点击 view 到应用给出响应,UIKit框架内部发生了一系列的事件序列,这些事件序列和实现这个view的类结合,完成点击事件开始到界面更新的全过程。首先,我们研究一下 UIKit 和 View 交互过程,在看一下UIKit框架与自定义 view类时的交互函数,以后自定义 View 时可以在这些函数中做一些事情。

(1)UIKit和View对象交互过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PYRaD1it-1597398352251)(/Users/meimei/Desktop/drawing_model.png)]

  • 用户触摸了屏幕,硬件接收到触摸事件

  • 硬件向UIKit框架报告该触摸事件

  • UIKit框架将这个触摸事件封装为一个UIEvent对象,并将它派发给相应的View(用户触摸的View)

  • 我们自定义的View类需要有相应代码处理该事件,根据自己的需求编写相应的处理事件代码来响应用户触摸操作。处理事件代码可能有以下内容。

    • 改变视图及其子视图的一些属性,比如:frame、bounds、alpha等等
    • 调用setNeedsLayout标记视图需要更新布局
    • 调用setNeedsDisplay或setNeedsDisplayInRect标记视图或者子视图需要重新绘制
    • 通知控制器可能有些数据已经改变
  • 如果View的几何图形发生变化,UIKit将根据下面规则更新view的子视图。

  • 如果view实现了layoutSubviews方法,UIKit将调用这个方法,在这个方法中我们一般调整view和其子视图的位置及大小。

  • 如果view中一部分子视图被标记为重画,UIKit将更新视图。

    对于自定义视图,UIKit将调用view中的drawRect: 代码,更新视图的可见内容,在这个函数中包含的内容应该是重画视图的代码,不应该做其他事情,例如更改视图的布局、改变视图的数据模型等等。对于系统视图,UIKit会在这个时候管理系统视图的绘制

  • 视图中的部分子视图的更新与视图中其他部分结合,发送给绘图硬件,让硬件来显示它们。

  • 绘图硬件在界面绘制这些内容

(2)UIKit框架与自定义 view类时的主要交互函数

  • 事件处理方法(touchesBegain:withEvent等方法)或手势识别器

  • layoutSubViews方法,一般自定义视图,在这个方法中布局子视图

    视图包含子视图,视图的尺寸发生变化时重载该函数

  • drawRect:方法,在这个方法中重新绘制一些视图

参考:View and Window Architecture

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员的修养

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值