UIKit控件
现在要开启我们的“UI工程师”之旅了。
写UI的第一步,就是要先知道苹果爸爸给我们提供了怎样的控件,毕竟每个控件都要自己完成的话也太耗费精力啦。
首先来一个经典的iOS控件继承图,接下来可以参照这个图进行讲解:
(因为我原本是学Android的,所以在讲解iOS的过程中可能会增加一些Android、Java的对比讲解)

一、UIResponder
(苹果的文档:UIResponder)
UIResponder是UIApplication、UIView、UIViewController的基类,它是响应和处理事件的一个抽象接口。
UIResponder构成UIKit应用程序的事件处理主干。UIApplicaiton也是Responder,事件发生的 时候,UIKit会将事件派分到应用程序的根Responder,根据事件响应链进行传播和响应。(责任链模式) 这些事件包括:触摸事件、移动事件、远程控制事件和按下事件。
事件分发详细讲解(//TODO:链接)
二、UIViewController
(苹果的文档:UIViewController)
1.职责
UIViewController应该对应安卓里面的Acitvity,但有些方面又不完全一致。
ViewController的主要工作包括:
- 更新view、管理view、修改view数据
- 响应用户的交互
- 管理view布局、调整view大小
2.对比UIViewController和Activity
在这里我基于我目前的理解,做一个简单的对比(往后还有新的理解体会,会继续补充):
UIViewController:(iOS)
- 每个UIViewController都有对应的一个View
- 每个应用程序都有一个rootViewController,可以为它添加子ViewController,可以调用方法来弹出新的ViewController
- UIViewController可以嵌套,层级可以由程序员控制
- deepLink(深度链接,其他应用可以直接链接到某一应用的指定页面)的话,还需要自己处理跳转(等我先学学)
Activity:(安卓)
- 每个Acitivity都有一个RootView(ViewGroup)
- 每个App可以有多个任务栈和多个Activity,Acitivity之间的关系相对平等,不可嵌套
- Activity是四大组件之一,一系列的Activity由任务栈管理
- Activity可以作为一个共享的界面组件给其他应用程序使用
- Android也有对应的deepLink机制
3.常接触到的UIViewController
- UITableViewController
- UINavigationController
- UIImagePickerController
- UIVideoEditorController
- UITabBarController
- UICollectionViewController
三、UIView
(苹果的文档:UIView)
UIView在安卓里面就是对应View,但是安卓还区分了View和ViewGroup,只有ViewGroup能够添加子View,而iOS并不区分这两者。
1.View的渲染
UIView在界面中表示一个矩形的图形控件,作为事件响应的一个单元存在;管理控件图形的显示和,但不做具体的显示和渲染工作,显示内容的工作交给CALayout来做。

对于这点我觉得安卓的图形渲染和显示也与这个有类似,安卓是这样的:
- 安卓中,View和ViewGroup负责参与事件响应链,还负责对自己及子View的进行mesure大小测量、layout布局、以及draw发起绘制(并不实际参与绘制)。
- View不负责显示,而负责做显示工作的是surface(绘图表面)。但是View和surface并不是一一对应的,正常情况下,一个Activity只有一个surface,里面的所有View都在同一个surface上进行绘制(但是每个View都会有自己的canvas画布,作为绘制的入口),只有如SurfaceView这类视图控件,才会有自己独立的surface。
- SurfaceFlinger是安卓中的一个负责显示的服务,它与每个要显示的surface是服务端与客户端的关系,每一个surface在SurfaceFlinger中作为一个layer存在。
- SurfaceFlinger和需要显示服务的客户端之间通过一块匿名共享内存进行数据交换,这块匿名共享内存中被封装成一个个栈帧SharedBufferStack,每个栈帧表示一块绘图缓冲区,图像就在缓冲区中绘制。绘制完成后,前台显示的图像与在后台事先绘制好的图像交换,这样就完成了一帧的显示。这个机制叫双缓冲机制,能够防止在绘制过程中因为图像覆盖的原因导致的图像闪烁问题,不过实际上安卓并不止“双”缓冲,使用surfaceView来测试时,可以看到实际上是多个缓冲区图像在轮换。
以上提到的双缓冲机制十分常见,特别是在游戏这种对帧率有要求的程序中常有应用,iOS中的渲染也有用到。一个十分经典的示意图如下:
渲染的流程是这样的,硬件时钟会发出VSync(垂直同步信号),然后App的代码会使用CPU会去计算屏幕要显示的内容,之后将计算好的内容提交到GPU去渲染。随后,GPU将渲染结果提交到帧缓冲区,等到下一个VSync到来时将缓冲区的帧显示到屏幕上。
通常的屏幕显示是一秒60帧,如果CPU或者GPU没能在16.7ms时间内产生一帧画面,则会导致掉帧和界面卡顿。
掉帧的解决方式:
iOS:
- iOS的优化分为CPU和GPU两部分,CPU方面,将以下操作放到子线程中:
- 对象创建、调整、销毁
- 预排版(布局计算、文本计算、缓存高度等等)
- 预渲染(文本等异步绘制,图片解码等)
- GPU方面可以优化的有:
- 减少纹理渲染,视图混合的工作量
- 尽量避免GPU离屏渲染。通常GPU在做渲染的时候是很快的,但是涉及到离屏渲染的时候情况就可能有些不同,因为需要额外开辟一个新的缓冲区进行渲染,然后绘制到当前屏幕的过程需要做onscreen跟offscreen上下文之间的切换,这个过程的消耗会比较昂贵。GPU离屏渲染何时会触发呢?
- 为图层设置遮罩(layer.mask)
- 将图层的layer.masksToBounds / view.clipsToBounds属性设置为true
- 将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0
- 为图层设置阴影(layer.shadow *)。
- 为图层设置layer.shouldRasterize=true
- 具有layer.cornerRadius,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的图层文本(任何种类,包括UILabel,CATextLayer,Core Text等)。
- 使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现。
关于iOS为什么在进行这些操作时会离屏渲染,有篇文章讲得还不错:关于iOS离屏渲染的深入研究github上也有一篇讲性能优化的
Android:
- App的绘制工作一般都在主线程中进行,不要在主线程中做太多工作,这样会导致主线程无法按时接收到绘制的消息。
- 简化界面布局的复杂程度,使用来进行布局优化。
- 防止过度绘制。过度绘制是指画面中同一个像素,因为图像覆盖的原因被绘制多次。将控件的背景设置为透明可以让过度绘制的区域减少。可以在开发者选项中打开Show GPU Overdraw选项,查看过度绘制情况。
- 如果仍然不能满足性能需求,可以使用surfaceView等控件,它们允许在后台线程进行绘制。
2.各个VIew的详细介绍(慢慢补齐链接吧)
- UINavigationBar
- UIPickerView
- UIImageView
- UIControl
- UIButton
- UITextField
- UISegmentedControl
- UISwitch
- UISlider
- UIDataPicker
- UIPageControl
- UISrcollView
- UITableView
- UICollectionView
- UITextView
- UITableViewCell
- UIActivityIndicatorView
- UIProgressView
- UILabel
- UIWebView
- UISearchBar
- UIAlertView
- UIWindow
- UIActionSheet
- UITableBar
- UIToolBar
四、UIApplication
(苹果的文档:UIApplication)
在安卓中也有Application类,我认为职责应该差不多。
- UIApplication对象是应用程序的象征。
- 每一个应用程序都有自己的UIApplication对象,而且是单例。
- 一个iOS程序启动后创建的第一个对象就是UIApplication对象。
- 通过UIApplication *app = [UIApplication sharedApplication];可以获得这个单例对象。
- 利用UIApplication对象能进行一些应用级别的操作。
UIApplication详细讲解(//TODO:链接)
五、结语
至此,UIKit控件的简要介绍就结束了,链接的内容可以慢慢阅读,或者作查询用。
附上UIKit框架更完整的继承图:

六、参考文章: