iOS UI视图总篇 - IOS教程
这篇文章介绍 UI视图 相关的内容,所涉及的知识点目录如下:
一、UITableView 相关二、事件传递 & 视图响应三、图像显示原理四、卡顿 & 掉帧五、绘制原理 & 异步绘制六、离屏渲染一、UITableView 相关(一)重用机制
cell = [tableView dequeueReusableCellWithIdentifier:Identifier]
真正被重用的时候,才会调用 [cell prepareForReuse] 方法,cell 消失并不会调用 prepareForReuse 。
代码实现重用原理,基本原理是通过两个 NSMutableSet:
重用池正在使用的如果确定一个 cell 是一定会被创建的,那么可以提前对 Identifier 进行 registerClass 的操作,这样通过 dequeueReusableCellWithIdentifier: 取出来的 cell 一定是不为 nil 的。
(二)数据源同步问题在实际开发过程中,tableView 数据源的同步是一大令人头疼的问题,追加数据、变更数据、删除数据都有可能导致 tableView crash,所以处理好 数据源同步问题 ,是掌握 tableView 的关键。
一个最常见的crash问题是 数据源变更 没有放到 主线程,导致 tableView UI 刷新时, 数据源已经不是触发 UI 刷新时的数据,导致数据不匹配 crash。
那么数据源同步有哪两种方案呢?
并发访问、数据拷贝以下面这个 case 为例:
大致解释一下这里的情况:
子线程正在进行网络请求操作,比如正在上传一个视频,但上传过程中用户把这个视频给删除了,因为上传是异步的,我们基本不可能在清理上传数据时,取消掉上传的网络请求。
如果这时网络回包上传成功了,那么就会把数据抛给主线程,重新刷新 UI ,这样就会把刚刚我们实际上已经删除的数据又加回来了。
上面这个问题在处理发表流程中非常常见,是一个基本必须处理的坑,那么这个问题要怎么处理呢?大家看一下下面的流程图:
清楚怎么处理了吗?
就是在用户删除本地数据的时,把这个数据拷贝一份塞到 deleteArray 中,等到网络请求回包后,这个数据能不能展示,需要经过 deleteArray 的过滤。
串行访问串行的思路很简单,就是在用户手动处理数据变更时,必须等到子线程操作完成,才能进行下一步操作。
对比并行方案 和 串行方案 有着不同的应用场景,一般来说:
耗时比较长的异步操作,可以采用 并行方案 ,比如: CDN上传;耗时比较短的异步操作,可以采用 串行方案 ,比如: cgi访问。二、事件传递 & 视图响应(一)UIView 和 CALayer 的关系与区别UIView = CALayer + 响应链
展示布局实际上是 CALayer.contents 决定的,contents对应一个位图。
你可能会说 UIView 也有 BackgroundColor 属性,那又是为什么呢?
实际上 UIView.backgroundColor == CALayer.backgroundColor ,也就是说 UIView.backgroundColor 是对 CALayer.backgroundColor 同名属性方法的一个封装。
(二)事件传递通过单一设计原则,明确了 UIView 和 CALayer 的分工。
谁来响应手势,和我喊了一个人的名字,谁来响应是一个事情:
首先是某一篇区域的人听到我喊了这个人的名字,然后是这个人自己来回应我。
所以手势响应需要两个步骤:明确手势的响应区间 >> 决定哪个元素来响应。
所以也就对应着两个方法:
(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
那这里实际上存在一个问题:pointInside: withEvent: 响应顺序是如何呢?
类比到我喊人的名字,但有8个区域,那这8个区域按照什么样的顺序判断是否响应我呢?
通过加入到视图上的顺序,「倒序」遍历视图的 hitTest:withEvent: 的方法如果 hitTest:withEvent: 返回的是 nil ,那么就按顺序继续「倒序」遍历,直到没有可响应的对象为止。
那你可能好奇了,你说的这个顺序中并没有说到 pointInside:withEvent: ,它又有什么用呢?
hitTest:withEvent: 底层会调用 pointInside:withEvent: 方法,判断手势在不在控件上,如果你已经优先实现了 hitTest:withEvent: ,那么就不会再调用 pointInside:withEvent: 方法了,可以理解为一个控件只需要实现其中一个方法,就能达到自定义事件传递的效果。
hitTest:withEvent: 系统实现流程视图事件响应知识延伸:UIApplication 和 UIApplicationDelegate 的关系main() 函数中执行了 UIApplicationMain() 函数:
int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }
那 UIApplication 有什么用呢?
UIApplication 是应用程序的象征,每一个 App 都有自己的 UIApplication 对象,而且是单例:
通过[UIApplication sharedApplication]可以获得这个单例对象,这也是 iOS 程序启动后创建的第一个对象。
通过 UIApplication 对象,我们可以进行一些系统级别的操作:
applicationIconBadgeNumber:设置应用程序图标右上角的红色提醒数字那 UIApplication 和 UIApplicationDelegate 之间有什么关系呢?
1.UIApplication 是 App 和 系统 之间通信的一个接口
2.所有 ViewController 默认遵守了 UIApplicationDelegate
3.一旦有系统级别的消息要通知当前页面,通过 UIApplication 通知它的 delegate 对象,让 delegate 对象来响应这些系统事件,包括:
应用程序的生命周期事件(如程序启动和关闭)系统事件(**电)内存警告接收到远程通知从其它应用程序跳入三、图像显示原理渲染路线CPU 和 GPU 两个硬件是通过总线连接起来的,CPU将计算后的「bitmap」经过「总线」输出给「GPU」
GPU拿到「bitmap」后,进行图层渲染,将生成的结果放到「frame buffer」中
最后由硬件控制器根据 sync 信号,在指令之前去 「frame buffer」提取显示内容,最终显示到屏幕上。
举例:
绘制好的内容最终会通过 Core Animation 框架 提供给 GPU OpenGL 渲染管线。
CPU 在渲染中承担的工作布局UI布局(frame设置、label文字size计算等)文本计算绘制调用 drawRect: 方法进行绘制,利用Quartz 2D提供的API绘制图形编解码比如 setImage: 解码图片提交位图GPU 在渲染中承担的工作四、卡顿 & 掉帧(一)UI 卡顿背后的原因60fps,也即60帧,也即16.7ms就要产生一帧动画,也就是在这 16.7ms 内CPU和GPU协同产出一帧数据,如果没有按时产出,那就会导致掉帧。
为什么是60fps ? 这是因为人眼与大脑之间的协作无法感知超过60fps的画面更新
(二)滑动优化方案1.从CPU角度进行优化对象创建、销毁预排版(布局计算、文本计算)预渲染(文本等异步绘制、图片编解码等)把不必要的工作放到子线程去做,让 CPU 有更多精力和时间去响应用户的交互。
2.从GPU角度进行优化纹理渲染视图混合五、绘制原理 & 异步绘制(一)UIView 的绘制原理(二)异步绘制关键方法:displayLayer
[layer.delegate displayLayer:]
代理负责生成对应的 bitmap设置该 bitmap 作为 layer.contens 属性的值六、离屏渲染(一)什么是离屏渲染,如何解释离屏渲染?当我们设置某一些 UI 视频时,如果指令是在「未全部合成」之前不能展示的,那么就触发了「离屏渲染」,常见的比如:圆角(当和maskToBounds一起使用)、蒙层、遮罩等,
离屏渲染 的概念起源于 GPU 层面,指的是:GPU在当前屏幕缓冲区以外新开辟了一个缓冲区进行渲染操作。
(二)为什么要避免离屏渲染?触发离屏渲染时,毫无疑问会增加GPU的工作量,而增加GPU的工作量,可能会导致 CPU+GPU 生成单个帧的总工作时间加起来超过 16.7ms, 会导致掉帧或卡顿。而且:
离屏渲染 创建了新的渲染缓冲区,会造成内存更多开销多通道渲染管线,最终还需要把多通道管线进行合成,这会增加 GPU 的负担。这篇关于iOS UI视图总篇的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持
还没有评论,来说两句吧...