美文网首页
Tangram分析一LazyScrollView

Tangram分析一LazyScrollView

作者: tom555cat | 来源:发表于2019-04-03 21:20 被阅读0次

    Tangram的组织结构

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

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

    • id<TMLazyScrollViewDataSource> 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:的处理流程可以用下图来表示:
    reloadData流程中的-recycleItems:newVisibleMuiIDs:
    reloadData流程中的-generateItems:的处理流程可以用下图来表示:
    reloadData流程中的-generateItems:
    setConentOffset方法的实现流程
    1. 用_lastContentOffset记录了上次滑动的contentOffset,当当前contentOffse.y和_lastContentOffset.y的差值绝对值大于LazyBufferHeight时,更新_lastContentOffset为当前的contentOffset,并调用assembleSubviews

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

    setConentOffset流程中的-recycleItems:newVisibleMuiIDs:的处理流程可以用下图来表示:
    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<TangramItemModelProtocol> *)model forLayout:(UIView<TangramLayoutProtocol> *)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<UIView *> *_visibleItems;
    • NSMutableSet<NSString *> _newVisibleMuiIDs;
    • TMLazyReusePool *reusePool;
    • id<TMLazyScrollViewDataSource> dataSource;
    方法:
    • reloadData
    • setConentOffset
    • dequeueReusableItemWithIdentifier

    相关文章

      网友评论

          本文标题:Tangram分析一LazyScrollView

          本文链接:https://www.haomeiwen.com/subject/fiqtiqtx.html