背景:列表中有很多卡片,其中部分连续的卡片可以为一个模块,这个模块可以插在列表的任何位置,也可有任意多个模块。当滑动到该位置时,会将模块的第一个卡片悬浮在顶部,滑出去时悬浮的view会随着滑出去。
业务框架简介: viewModel:负责网络加载,数据维护。例如列表中的每个卡片要对应一个object,那么该角色会维护一个Array。 VC:列表渲染。
分析
- 首先要考虑的是该需求可以插在任意位置,且可能出现在任意分页中,而且列表的卡片可能随时被用户删除或增加,所以在滑动列表的回调要实时判断区间。
- 滑动时的回调是根据帧数来回调的,而不是根据滑动多少距离来回调的,所以我们不能将区间的起始结束标记在cell中,否则可能会在一次滑动距离过大时,并没有检测到起始或结束的标记位滑过了。
- 悬浮的view可能有多个种类,所以我们需要维护一个container view,来承载不同类型的悬浮view。
一个粗暴的实现
首先在viewModel中配置列表的model时,将区间的起始和结束位置,分别插入一个标记model,再在起始model下插入一个悬浮view的对应model。 那么之后只需要在滑动的时候根据当前的位置来搜索区间就好了。
- 配置object的array,插入悬浮区间的start model 和 end model。
scrollViewDidScroll
里 向上向下搜索起始startOffset
和结束位置endOffset
,并记录。- 根据
startOffset
和endOffset
来判断当前是在悬浮区间内还是外,如果是内根据startOffset
对应的index来找到要显示的model就可以。 - 根据model来生成并显示悬浮的view。
问题分析
滑动回调较为频繁,如果array过长,或机型过老,可能会很卡,对CPU消耗较大。
优化
耗时的操作主要在寻找区间标记位的循环操作,以及渲染悬浮view的时候一些创建和释放操作,那么针对这个问题可以简单优化下。
- 因为列表渲染后的UI和models是对应的,也就是不管列表怎么变,其悬浮区间和列表中的models是相关的,那么我们可以做些预处理的操作。
- 预处理:通过KVO监测列表的array变化,每次变化后遍历array,记录悬浮的各个区间,并记录悬浮的各个下标。通过控件换时间。
- 这样在
scrollViewDidScroll
中根据当前位置判断是否在悬浮区间,如果在直接根据悬浮区间的下标找到在列表的下标,直接找到model来显示。 - 通过hidden属性来控制悬浮的view的显示与隐藏,减少alloc等操作。
这样的好处是通过预处理的操作来减少每次滑动时的遍历操作,通过hidden来减少alloc等操作。
结果
通过instrument的数据对比,优化后的数值大概是前值的20%左右。