Tangram分析一LazyScrollView

Tangram的组织结构

Tangram分析一LazyScrollView_第1张图片
Tangram结构图

从Tangram结构图上看,主要分为页面(TangramView+TMlazyScrollView),布局(layout)和组件(element)三部分。页面是由继承自TMLazyScrollView的TangramView构成,TMLazyScrollView是一个独立的视图组件,本文主要内容为如何实现一个TMLazyScrollView。

模型

TMLazyItemModel

TMLazyItemModel是TMLazyScrollView中使用的基本模型,存储着TMLazyScrollView中的view对应的布局信息和唯一标识符,模型中包含的属性主要有:

  • CGRect absRect; // view在LazyScrollView中相对于LazyScrollView的frame
  • NSString *muiID; // view在LazyScrollView的唯一id
  • 什么是muiID?
    muiID的是由这几部分构成的:
    layout.layoutType + model.itemType + model.reuseIdentifier + layout.layoutIdentifier + model.index(将lazyScrollView中的view视为同一层级之后的下标)
    这里的model就是lazyItemModel,由于与view一一对应,所以model的index与view的index也是一样的。

TMLazyModelBucket

TMLazyModelBucket是基本模型TMLazyItemModel上的一层封装,是比TMLazyItemModel层级更高的一个模型。是将origin.y在i*bucketHeight~(i+1)bucketHeight范围内的TMLazyItemModel放在一个集合里,然后所有的集合构成的一个数组。所以需要包含的属性主要有:

  • NSMutableArray _buckets; // 0 ~ bucketHeight中的model保存在index为0的set中。bucketHeight~2bucketHeight中的model保存在index为1的set中。

需要提供的方法应该包括:

  • -(void)addModel:(TMLazyItemModel *)itemModel;

控制器

TMLazyScrollView

TMLazyScrollView就是控制器,管理着的模型包括TMLazyModelBucket,TMLazyScrollView上的视图view,以及为了view重用而衍生的view唯一标识符muiID,所以包含的主要属性有:

  • TMLazyModelBucket *_modelBucket; // 通过TMLazyModelBucket,保存了所有的lazyItemModel。
  • NSMutableSet *_visibleItems; // 这里边存放的是可见范围的view视图
  • NSMutableSet _newVisibleMuiIDs; // 这个reloadData中在新的可视范围内的待处理(需要创建element或者重新刷新element)的model的MuiID的set,当generateItems一个 MuiID(创建element或者刷新element)之后,就会将这个MuiID从这个集合中删除掉。
  • TMLazyReusePool *reusePool; // 保存复用的view
下面是一些次要属性,主要用来保存业务处理过程中临时保存的数据,可以先放着不看。
* NSMutableSet *_needReloadingMuiIDs;    // 存储需要刷新内容的item对应的muiID
* NSSet *_lastInScreenVisibleMuiIDs;     // 存储上次可视范围内的TLMLazyItemModel的MuiIDs。
* NSMutableSet *_inScreenVisibleMuiIDs;  // 当前可视范围内的TMLazyItemModel的MuiIDs。

TMLazyScrollView还需要一个dataSource代理,代理需要实现一些方法用来提供一些必要的数据:

  • id dataSource;
    dataSource需要实现的方法有:
    1> - (NSUInteger)numberOfItemsInScrollView:(TMLazyScrollView *)scrollView
    返回有多少个model。
    2> - (TMLazyItemModel *)scrollView:(TMLazyScrollView *)scrollView itemModelAtIndex:(NSUInteger)index
    返回第index个model。
    3> - (UIView *)scrollView:(TMLazyScrollView *)scrollView itemByMuiID:(NSString *)muiID
    根据muiID返回view。

TMLazyScrollView对外提供的方法有:

  • -reloadData() // 获取数据后或者数据更新后加载全部数据
  • -setContentOffset: // 手动滑动lazyScrollView之后视图的变化
reloadData方法的实现流程
  1. [self storeItemModelsFromIndex:0]
    清理所有本地数据;将所有的view对应的lazyModel存放进_modelBucket中,需要dataSource的方法1>和2>来提供lazyModel。

  2. -assembleSubviews:minY:maxY:
    统计当前可见范围内的model的muiID集合newVisibleMuiIDs。

2.1 -recycleItems:newVisibleMuiIDs: (isReload参数为YES)
遍历当前的可视view数组,_visibleItems

2.1.1 如果其中的view的muiID在将要出现的muiIDs集合newVisibleMuiIDs中,那么这个view只需要刷新其中的数据即可,将其muiID加入_needReloadingMuiIDs中。
2.1.2 如果其中的view的muiID不在将要出现的muiIDs集合newVisibleMuiIDs中,那么这个view就需要离开屏幕

2.1.2.1 此时需要调用view的mui_didLeave()方法
2.1.2.2 如果此view的reuseIdentifier.length>0,也就是说这个view是需要被重用的,那么就将其隐藏,并且加入reusePool中,将此element从_visibleItems数组中移除掉。
2.1.2.3 如果此itemView不打算重用,将其muiID加入_needReloadingMuiIDs中。

2.2 -generateItems: (isReload参数为YES)
获取view

2.2.1 对于_newVisibleMuiIDs中的任一muiID

2.2.1.1 如果不在_visibleItems中,或者需要刷新,则需通过代理dataSource的方法3>获取view,获取到itemView之后,(如何通过代理dataSource获取view?)【TMLazyScrollView如何通过代理获取element?】

2.2.1.1.1 调用view的方法mui_afterGetView
2.2.1.1.2 设置view的muiID,并设置hidden为NO,就是要让其显示出来,
2.2.1.1.3 如果该view之前没有显示在屏幕上,那么这次需要将其加入_visibleItems数组中
2.2.1.1.3 将其重_needReloadingMuiIDs中删除

2.2.1.2 对于newVisibleMuiIDs中的任一muiID,如果在_visibleItems中并且不需要刷新,那么不需要做其他操作

2.2.2 将muiID从_newVisibleMuiIDs总移除,如果_newVisibleMuiIDs不为空,则继续调用generateItems

reloadData流程中的-recycleItems:newVisibleMuiIDs:的处理流程可以用下图来表示:
Tangram分析一LazyScrollView_第2张图片
reloadData流程中的-recycleItems:newVisibleMuiIDs:
reloadData流程中的-generateItems:的处理流程可以用下图来表示:
Tangram分析一LazyScrollView_第3张图片
reloadData流程中的-generateItems:
setConentOffset方法的实现流程
  1. 用_lastContentOffset记录了上次滑动的contentOffset,当当前contentOffse.y和_lastContentOffset.y的差值绝对值大于LazyBufferHeight时,更新_lastContentOffset为当前的contentOffset,并调用assembleSubviews

  2. -assembleSubviews:minY:maxY:
    剩余流程同reloadData。

setConentOffset流程中的-recycleItems:newVisibleMuiIDs:的处理流程可以用下图来表示:
Tangram分析一LazyScrollView_第4张图片
setConentOffset流程中的-recycleItems:newVisibleMuiIDs:
setConentOffset和reloadData的区别

区别在于recycleItem:newVisibleMuiIDs:中的第一个参数isReload,手动滑动isReload为NO,reloadData中的isReload为YES。

  • 如果_visibleItems中的element还要呆在屏幕上:
    1> 对于reloadData,仍然需要刷新其中内容 (加入_needReloadingMuiIDs数组中)
    2> 对于手动滑动,不需要刷新
  • 如果_visibleItems中的element不需要呆在屏幕上
    1> 对于reloadData,仍然需要刷新其中内容 (加入_needReloadingMuiIDs数组中)
    2> 对于手动滑动,不需要刷新
TMLazyScrollView通过self.dataSource获取view

具体调用流程为:
1 - (UIView *)scrollView:(TMLazyScrollView *)scrollView itemByMuiID:(NSString *)muiID
该方法是TangramView实现TMLazyScrollView的dataSource的3>代理方法,TangramView在实现中会调用VC实现的返回itemView代理方法

2 - (UIView *)itemInTangramView:(TangramView *)view withModel:(NSObject *)model forLayout:(UIView *)layout atIndex:(NSUInteger)index
这是VC需要实现的代理方法。在其中,可能会调用dequeueReusableItemWithIdentifier:方法, 调用的是TMLazyScrollView的方法dequeueReusableItemWithIdentifier:(muiID:)

2.1 - (UIView *)dequeueReusableItemWithIdentifier:(NSString *)identifier muiID:(NSString *)muiID
如果当前需要的(newVisibleMuiIDs中的muiID)view在屏幕上,则直接使用该view;
如果不在屏幕上,则从reusepool中拿一个,返回之;
如果什么也没有,则直接返回nil。

2.2 如果dequeueReusableItemWithIdentifier:返回结果非空,则用model去刷新该element。
reuseableView = [TangramDefaultDataSourceHelper refreshElement:reuseableView byModel:model layout:layout tangramBus:self.tangramBus];

2.3 如果dequeueReusableItemWithIdentifier:返回结果为空,则需要创建一个element,并用model数据去填充该element。
reuseableView = [TangramDefaultDataSourceHelper elementByModel:model layout:layout tangramBus:self.tangramBus];

从这个角度来看,TMLazyScrollView还需要实现一个重用view的方法,dequeueReusableItemWithIdentifier:(muiID:)

dequeueReusableItemWithIdentifier:(muiID:)方法实现流程
  1. 通过identifier从reusepool中查找view
    1.1 如果找到,调用view的prepareForReuse
  2. 返回找到的view或者nil

总结

模型:

  • TMLazyItemModel
  • TMLazyModelBucket

控制器:

  • TMLazyScrollView
属性:
  • TMLazyModelBucket *_modelBucket;
  • NSMutableSet *_visibleItems;
  • NSMutableSet _newVisibleMuiIDs;
  • TMLazyReusePool *reusePool;
  • id dataSource;
方法:
  • reloadData
  • setConentOffset
  • dequeueReusableItemWithIdentifier

你可能感兴趣的:(Tangram分析一LazyScrollView)