写界面可以说是每位移动应用开发者的基本功,也是一位合格移动应用开发者绕不过去的坎。但就如不是每一位开发者都能够成为合格的开发者一样,本人在 不同的团队中发现,甚少有人能够编写出合格的UI代码;而非常奇怪的是,在很多的开发者论坛上看到我们移动开发者更多关注于某个控件或者是动画,但却很少 看到深入剖析UI机制,指导UI开发的文章。
由于界面涉及到的方面实在过于广泛,本文不可能事无巨细,一一道来,所以本文先立足于点,深入剖析iOS UI系统中不被重视却非常重要的机制,帮助本文读者对iOS的UI系统有整体了解;进而以点带面,拓展到UI逻辑设计和架构设计模式的讨论;最后读文而有 所思有所得,设计开发出高效、易用、流畅的UI模块。
本文章节如下:
- 基础与本质:说明普遍意义上的UI系统的三大模块,让读者从整体上对UI系统有清楚的认识。
- View:深入View的内部机制,View与Layer之间的关系,以及Offscreen Render;
- ViewController:讲解ViewController在UI系统中所扮演的角色,以及UI架构设计中ViewController运用和实践;
- MVC、MVP、MVVM:简单分析三种主流的架构设计模式及其异同,并简单提出了一些做架构设计意见和想法;
- 总结。
各章节间没有必然的联系,读者可以选择感兴趣章节阅读。
1. 基础与本质
终端App开发区别于后端开发最大的不同,就是终端开发很大部分的逻辑是为用户提供界面以供人机交互,即所谓的UI(User Interface)。所以所有的UI架构主要关注三大模块:界面布局管理,渲染及动画、事件响应;
1.1 布局管理
即在规定的坐标系统上,按照一定的层级顺序位置大小排布在容器内。一个UI系统必然有个基于坐标的布局管理系统,不管是Windows、Sysbian,还是Andorid、iOS。好的布局管理机制直接影响界面逻辑实现的难易程度;
我们现在日常接触到的App的UI坐标系统都是二维的,我们现在玩的3D游戏,受限于二维的展示屏幕,所以实质上只是三维在二维上的映射投影。我们 一直在往更高的维度发展:全息影像、Hololens等等。在此可以设想下,未来我们构建界面的布局管理很可能就是基于真实三维坐标。
1.2 动画及渲染
UI之所以叫User Interface,就是因为UI通过视觉上的展示,为用户提供信息。这些信息的展示需要通过一系列复杂的计算,最后操作液晶体展示在显示屏上,这一系列过程就是渲染和动画;
下图就是应用界面渲染到展示的流程:
引自WWDC2014 #419 Advanced Graphics and Animations for iOS Apps
这里不展开来讲,推荐没看过的同学都认真观看,能够很好的理解渲染流程和界面优化;
推荐资料:
1.3 事件响应
UI除了展示信息之外,还需要接收并响应用户的点击、手势、摇晃等事件,经过一系列操作后更新展示信息,展示给用户;正确及时地响应用户的操作并给 予反馈,是良好用户体验的保证。为何Android设备普遍给人的感觉比iOS设备要卡,其中一个主要的原因是iOS系统将响应用户事件放在主线程的最高 优先级。
1.4 UI系统架构
从整体理解了上述三个方面,你会对UI架构有系统认识。iOS中的UI系统架构如下:
引自WWDC2014 #419 Advanced Graphics and Animations for iOS Apps
2. View
UIView是UIKit中最基本控件,就如同NSObject基本上是Cocoa库内所有类的基类一样,UIView也是UIKit中所有界面控 件的基类。只要你愿意,你甚至只用UIView就可以搭建你的App(不过iOS9做了约束,必须设置keyWindow的 rootViewControler)。
一般来说,熟练掌握常用的UIView子类控件(如UIButton, UIImageView, UILabel等)就足以应付90%的界面编码需要。但想要编写出高效、优美的界面代码,还需要更深入的了解。既然要深入,本文假设你对UIView已经 有了初步的了解,至少使用写过几个完整的页面;基于此设定下,本文讨论聚焦于以下几点:
1) UIView 与 CALayer:讨论UIView背后的CALayer,了解CALayer与UIView的关系及渲染流程;
2) Offscreen Render:阐述什么是Offscreen Render(离屏渲染),以及一些避免离屏渲染的方法;
3) UIResponser:讨论UIView和UIViewController的父类UIResponser,分析iOS设备上的事件响应链;
4) 设计与实践:结合本人开发实践经验,说明在UIView应用中好的设计实践规则;
参考:View Programming Guide for iOS
2.1 UIView 与 CALayer
我们应该都知道每个UIView都包含了一个CALayer,就算你没直接看过CALayer,应该也使用过。比如给一个View切个圆角:view.layer.cornerRadius = 5.0f;
;加个边框:view.layer.borderWidth = 1.0f; view.layer.borderColor = [UIColor darkGrayColor].CGColor;
,这里使用的layer就是CALayer。
CALayer是QuartzCore库内的类,是iOS上最基本的绘制单元;而UIView只是CALyer之上的封装,更准确的来 说,UIView是CALyer的简版封装,加上事件处理的集合类。事件处理我们下一节再讨论,这里的简版封装如何理解,为什么不直接使用 CALayer?
首先,如上一段所述,CALayer是最基本的绘制单元,每一个UIView都有一个CALayer的变量(public var layer: CALayer { get }
),UIView的渲染实质就是这个layer的渲染。我们可以看看的类定义,里面有很多属性(变量)及方法在中可以找到几乎一模一样的对应;如属性变量frame
、hidden
,方法public func convertPoint(p: CGPoint, fromLayer l: CALayer?) -> CGPoint
等;但也有更多的属性方法是UIView所没有的,这里就一一列举了。我们可以看到UIView其实是把常用的接口(属性和方法)暴露出来了,让UIView更为易用。
其次,我们知道iOS平台的Cocoa Touch 是源于OS X平台的Cocoa), 是在Cocoa的基础上添加了适用于移动手机设备的手势识别、动画等特性;但从底层实现上来说,Cocoa Touch与Cocoa共用一套底层的库,其中就包括了QuartCore.framework;但QuartCore.framework一开始就是为 OS X设计的,所以其中有部分特性是不适合做移动