全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

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

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。

@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 ,这是个什么东西呢?我们看一下代码:

@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)

//索引过的标识,在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内部原理分析

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

根据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个像素后才会指定查找视图并显示的动作。举个例子,如下图,红圈是应该显示的区域。

如上图所示,现在已知的是红圈顶边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。

- (UIView *)scrollView:(TMMuiLazyScrollView *)scrollView itemByMuiID:(NSString *)muiID;

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

- (UIView *)dequeueReusableItemWithIdentifier:(NSString *)identifier

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

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


# iOS  # 滚动视图的复用  # ios解决复用问题 


相关文章: 网站制作说明怎么写,简述网页设计的流程并说明原因?  如何在企业微信快速生成手机电脑官网?  合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?  香港网站服务器数量如何影响SEO优化效果?  ,怎么用自己头像做动态表情包?  如何通过wdcp面板快速创建网站?  如何用AWS免费套餐快速搭建高效网站?  建站之星安全性能如何?防护体系能否抵御黑客入侵?  如何用手机制作网站和网页,手机移动端的网站能制作成中英双语的吗?  上海网站制作开发公司,上海买房比较好的网站有哪些?  建站DNS解析失败?如何正确配置域名服务器?  如何通过PHP快速构建高效问答网站功能?  怀化网站制作公司,怀化新生儿上户网上办理流程?  如何零成本快速生成个人自助网站?  如何在云虚拟主机上快速搭建个人网站?  实现虚拟支付需哪些建站技术支撑?  标准网站视频模板制作软件,现在有哪个网站的视频编辑素材最齐全的,背景音乐、音效等?  如何在阿里云服务器自主搭建网站?  C#怎么创建控制台应用 C# Console App项目创建方法  微网站制作教程,我微信里的网站怎么才能复制到浏览器里?  如何在局域网内绑定自建网站域名?  如何通过免费商城建站系统源码自定义网站主题与功能?  建站主机系统SEO优化与智能配置核心关键词操作指南  建站之星logo尺寸如何设置最合适?  建站之星在线客服如何快速接入解答?  如何快速辨别茅台真假?关键步骤解析  广州网站建站公司选择指南:建站流程与SEO优化关键词解析  家庭建站与云服务器建站,如何选择更优?  儿童网站界面设计图片,中国少年儿童教育网站-怎么去注册?  建站之星ASP如何实现CMS高效搭建与安全管理?  如何高效搭建专业期货交易平台网站?  建站一年半SEO优化实战指南:核心词挖掘与长尾流量提升策略  如何用虚拟主机快速搭建网站?详细步骤解析  已有域名如何快速搭建专属网站?  如何通过建站之星自助学习解决操作问题?  建站之星2.7模板:企业网站建设与h5定制设计专题  如何快速搭建高效WAP手机网站?  网站制作网站,深圳做网站哪家比较好?  创业网站制作流程,创业网站可靠吗?  ,怎么在广州志愿者网站注册?  如何在腾讯云服务器上快速搭建个人网站?  高端建站三要素:定制模板、企业官网与响应式设计优化  C++ static_cast和dynamic_cast区别_C++静态转换与动态类型安全转换  Python如何创建带属性的XML节点  如何用西部建站助手快速创建专业网站?  如何通过VPS搭建网站快速盈利?  建站之星会员如何解锁更多建站功能?  如何自定义建站之星网站的导航菜单样式?  车管所网站制作流程,交警当场开简易程序处罚决定书,在交警网站查询不到怎么办?  简历在线制作网站免费版,如何创建个人简历? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。