服务器之家

服务器之家 > 正文

详解iOS 滚动视图的复用问题解决方案

时间:2021-02-28 14:18     来源/作者:code_xzh

lazyscroll是什么

lazyscrollview 继承自scrollview,目标是解决异构(与tableview的同构对比)滚动视图的复用回收问题。它可以支持跨view层的复用,用易用方式来生成一个高性能的滚动视图。

为什么要用lazyscrollview

我们在做首页的时候,往往展示的东西会很多,随着view数量逐渐膨胀,没有一套复用回收机制的scrollview已经影响到性能了,迫切需要处理对scrollview中view的复用和回收。使用tableview只能用来解决同类cell的展示,然而在实际的场景中在scrollview里面,view的种类往往会比较多,所以使用tableview不适合我们的场景。
而uicollectionview本身的布局和复用回收机制不够灵活,用起来也较为繁琐。所以诞生了lazyscrollview去解决这个问题。这也是天猫ios客户端的首页落地方案。

lazyscroll使用

lazyscrollview的使用和tableview很像,不过多了一个需要实现的方法:返回对应index的view 相对lazyscrollview的绝对坐标。

实现lazyscrollviewdatasource

类似tableview的用法,我们需要使用方实现lazyscrollviewdatasource的delegate。

?
1
2
3
4
5
6
7
8
@protocol tmmuilazyscrollviewdatasource <nsobject>
@required
//scrollview展示item个数
- (nsuinteger)numberofiteminscrollview:(tmmuilazyscrollview *)scrollview;
//要求根据index直接返回rectmodel
- (tmmuirectmodel *)scrollview:(tmmuilazyscrollview *)scrollview rectmodelatindex:(nsuinteger)index;
//返回下标所对应的view
- (uiview *)scrollview:(tmmuilazyscrollview *)scrollview itembymuiid:(nsstring *)muiid;

lazyscrollview的核心是在初始状态就得知所有view应该显示的位置。第一个方法很简单,获取lazyscrollview中item的个数。第二个方法需要按照index返回tmmuirectmodel ,它会携带对应index的view 相对lazyscrollview的绝对坐标。

这里出现了一个tmmuirectmodel ,这是个什么东西呢?我们看一下代码:

?
1
2
3
4
5
@interface tmmuirectmodel:nsobject
//转换后的绝对值rect
@property (nonatomic,assign) cgrect absrect;
//业务下标
@property (nonatomic,copy) nsstring *muiid;

这里有两个属性,absrect是lazyscroll中的view相对lazyscrollview的绝对坐标,muiid是这个view在lazyscrollview中唯一的标识符,可赋值也可不赋值。

第三个方法,返回view。

@interface uiview(tmmui)

?
1
2
3
4
//索引过的标识,在lazyscrollview范围内唯一
@property (nonatomic, copy) nsstring *muiid;
//重用的id
@property (nonatomic, copy) nsstring *reuseidentifier;

首先,我们在uiview之外加了一个category,这个category可以让view携带muiid和reuseidentifier,对于返回的view来说,只需要在乎对view的reuseidentifier赋值,muiid的赋值会在lazyscrollview中处理掉。reuseidentifier相同的view会被复用,如果这个view的reuseidentifier是nil或者空字符串,则不会被复用。

lazyscrollview内部原理分析

首先来看一个简单的案例:

详解iOS 滚动视图的复用问题解决方案

根据datasource获取所有的tmmuirectmodel

根据datasource的delegate,拿到所有的view应该被显示的位置。这一步,核心是拿到的位置是确定的。根据demo,我们观察从 0/1 - 2/3 之间这些view,这个时候lazyscrollview拿到的rect如下:

 

index 标号(muiid) rect
0 0/0 origin = (x = 25, y = 15), size = (width = 156, height = 150
1 0/1 origin = (x = 194, y = 15), size = (width = 156, height = 150)
2 0/2 origin = (x = 25, y = 180), size = (width = 156, height = 150)
3 0/3 origin = (x = 194, y = 180), size = (width = 156, height = 150
4 1/0 origin = (x = 5, y = 360), size = (width = 177.5, height = 150)
5 1/1 origin = (x = 192.5, y = 426), size = (width = 84, height = 84)
6 1/2 origin = (x = 192.5, y = 360), size = (width = 177.5, height = 56)
7 1/3 origin = (x = 286.5, y = 426), size = (width = 83.5, height = 84)
8 2/0 origin = (x = 25, y = 530), size = (width = 325, height = 150)
9 2/1 origin = (x = 25, y = 695), size = (width = 325, height = 150)
10 2/2 origin = (x = 25, y = 860), size = (width = 325, height = 150)

 

排序

拿到了这些位置之后,接下来做的事情就是排序。排序生成的索引会有两个:根据顶边(y)升序排序的索引和根据底边(y+height)降序排序的索引。

根据顶边(y)升序排序的索引

 

index 标号(muiid) rect
0 0/0 origin = (x = 25, y = 15), size = (width = 156, height = 150
1 0/1 origin = (x = 194, y = 15), size = (width = 156, height = 150)
2 0/2 origin = (x = 25, y = 180), size = (width = 156, height = 150)
3 0/3 origin = (x = 194, y = 180), size = (width = 156, height = 150
4 1/0 origin = (x = 5, y = 360), size = (width = 177.5, height = 150)
5 1/1 origin = (x = 192.5, y = 360), size = (width = 177.5, height = 56)
6 1/2 origin = (x = 192.5, y = 360), size = (width = 177.5, height = 56)
7 1/3 origin = (x = 286.5, y = 426), size = (width = 83.5, height = 84)
8 2/0 origin = (x = 25, y = 530), size = (width = 325, height = 150)
9 2/1 origin = (x = 25, y = 695), size = (width = 325, height = 150)
10 2/2 origin = (x = 25, y = 860), size = (width = 325, height = 150)

 

根据底边(y+height)降序排序的索引

 

index 标号(muiid) rect
0 2/2 origin = (x = 25, y = 860), size = (width = 325, height = 150)
1 2/1 origin = (x = 25, y = 695), size = (width = 325, height = 150)
2 2/0 origin = (x = 25, y = 530), size = (width = 325, height = 150)
3 1/0 origin = (x = 5, y = 360), size = (width = 177.5, height = 150)
4 1/2 origin = (x = 192.5, y = 360), size = (width = 177.5, height = 56)
5 1/3 origin = (x = 286.5, y = 426), size = (width = 83.5, height = 84)
6 1/1 origin = (x = 192.5, y = 426), size = (width = 84, height = 84)
7 0/2 origin = (x = 25, y = 180), size = (width = 156, height = 150)
8 0/3 origin = (x = 194, y = 180), size = (width = 156, height = 150
9 0/0 origin = (x = 25, y = 15), size = (width = 156, height = 150
10 0/1 origin = (x = 194, y = 15), size = (width = 156, height = 150)

 

查找

前两步是在执行完reload,在视图还没有生成的时候就开始做了,而接下来的步骤在要生成视图(初始化或滚动的时候)才会去做。

我们设定了buffer为上下各20,滚动超过20个像素后才会指定查找视图并显示的动作。举个例子,如下图,红圈是应该显示的区域。

详解iOS 滚动视图的复用问题解决方案

如上图所示,现在已知的是红圈顶边y是242,底边y是949,加上缓冲区buffer,应该是找222 - 969 之间的view。我们要做的是,找到底边y小于969的model和顶边y大于222的model,取交集,就是我们要显示的view。

采用的方法为二分查找,在根据顶边升序排序的索引中找949,找到的index为0(muiid为2/2),我们使用一个set,把根据顶边排序中index >= 0 的元素先放在这里。获取的set中包含的muiid为 0/0,0/1,0/2,0/3,1/0,1/1,1/2,1/3,2/0,2/1,2/2。

根据底边排序的索引中找222,找到的index为2,我们把index >= 2的元素放在另一个set,获取的set中包含的muiid为0/2,0/3,1/0,1/1,1/2,1/3,2/0,2/1,2/2

两个set取交集,得到的就是我们的resultset,这里面都是我们要显示view的model,它们的muiid是0/2,0/3,1/0,1/1,1/2,1/3,2/0,2/1,2/2。

回收、复用、生成

我们知道了应该显示哪些view,但是我们之后做的第一步是把不需要显示的view加入到复用池中。lazyscroll可以取到当前显示了的view,拿当前显示的view的muiid和将要显示view的model的muiid做对比,可以知道当前显示的view哪些应该被回收。

lazyscrollview中有一个dictionary,key是reuseidentifier,value是对应reuseidentifier被回收的view,当lazyscrollview得知这个view不该再出现了,会把view放在这里,并且把这个view hidden掉。

然后,用lazyscrollview会去调用datasource。

?
1
- (uiview *)scrollview:(tmmuilazyscrollview *)scrollview itembymuiid:(nsstring *)muiid;

复用还是不复用,是由datasource决定的。如果要复用,需要datasource方法内调用,即:

?
1
- (uiview *)dequeuereusableitemwithidentifier:(nsstring *)identifier

获取复用的view,这个方法取出来的view就是在上一段所说的dictionary中拿的。
最后我们看一下lazyscrollview的使用流程:找到所有view将要显示的位置 – 排序 – 查找应该显示的view – 回收 – 创建/复用。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://blog.csdn.net/xiangzhihong8/article/details/53898810

标签:

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
Intellij idea2020永久破解,亲测可用!!!
Intellij idea2020永久破解,亲测可用!!! 2020-07-29
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
返回顶部