Tangram分析一LazyScrollView

Tangram的組織結(jié)構(gòu)

Tangram結(jié)構(gòu)圖

從Tangram結(jié)構(gòu)圖上看,主要分為頁面(TangramView+TMlazyScrollView),布局(layout)和組件(element)三部分。頁面是由繼承自TMLazyScrollView的TangramView構(gòu)成,TMLazyScrollView是一個獨立的視圖組件,本文主要內(nèi)容為如何實現(xiàn)一個TMLazyScrollView。

模型

TMLazyItemModel

TMLazyItemModel是TMLazyScrollView中使用的基本模型,存儲著TMLazyScrollView中的view對應(yīng)的布局信息和唯一標(biāo)識符,模型中包含的屬性主要有:

  • CGRect absRect; // view在LazyScrollView中相對于LazyScrollView的frame
  • NSString *muiID; // view在LazyScrollView的唯一id
  • 什么是muiID?
    muiID的是由這幾部分構(gòu)成的:
    layout.layoutType + model.itemType + model.reuseIdentifier + layout.layoutIdentifier + model.index(將lazyScrollView中的view視為同一層級之后的下標(biāo))
    這里的model就是lazyItemModel,由于與view一一對應(yīng),所以model的index與view的index也是一樣的。

TMLazyModelBucket

TMLazyModelBucket是基本模型TMLazyItemModel上的一層封裝,是比TMLazyItemModel層級更高的一個模型。是將origin.y在i*bucketHeight~(i+1)bucketHeight范圍內(nèi)的TMLazyItemModel放在一個集合里,然后所有的集合構(gòu)成的一個數(shù)組。所以需要包含的屬性主要有:

  • NSMutableArray<NSMutableSet *> _buckets; // 0 ~ bucketHeight中的model保存在index為0的set中。bucketHeight~2bucketHeight中的model保存在index為1的set中。

需要提供的方法應(yīng)該包括:

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

控制器

TMLazyScrollView

TMLazyScrollView就是控制器,管理著的模型包括TMLazyModelBucket,TMLazyScrollView上的視圖view,以及為了view重用而衍生的view唯一標(biāo)識符muiID,所以包含的主要屬性有:

  • TMLazyModelBucket *_modelBucket; // 通過TMLazyModelBucket,保存了所有的lazyItemModel。
  • NSMutableSet<UIView *> *_visibleItems; // 這里邊存放的是可見范圍的view視圖
  • NSMutableSet<NSString *> _newVisibleMuiIDs; // 這個reloadData中在新的可視范圍內(nèi)的待處理(需要創(chuàng)建element或者重新刷新element)的model的MuiID的set,當(dāng)generateItems一個 MuiID(創(chuàng)建element或者刷新element)之后,就會將這個MuiID從這個集合中刪除掉。
  • TMLazyReusePool *reusePool; // 保存復(fù)用的view
下面是一些次要屬性,主要用來保存業(yè)務(wù)處理過程中臨時保存的數(shù)據(jù),可以先放著不看。
* NSMutableSet<NSString *> *_needReloadingMuiIDs;    // 存儲需要刷新內(nèi)容的item對應(yīng)的muiID
* NSSet<NSString *> *_lastInScreenVisibleMuiIDs;     // 存儲上次可視范圍內(nèi)的TLMLazyItemModel的MuiIDs。
* NSMutableSet<NSString *> *_inScreenVisibleMuiIDs;  // 當(dāng)前可視范圍內(nèi)的TMLazyItemModel的MuiIDs。

TMLazyScrollView還需要一個dataSource代理,代理需要實現(xiàn)一些方法用來提供一些必要的數(shù)據(jù):

  • id<TMLazyScrollViewDataSource> dataSource;
    dataSource需要實現(xiàn)的方法有:
    1> - (NSUInteger)numberOfItemsInScrollView:(TMLazyScrollView *)scrollView
    返回有多少個model。
    2> - (TMLazyItemModel *)scrollView:(TMLazyScrollView *)scrollView itemModelAtIndex:(NSUInteger)index
    返回第index個model。
    3> - (UIView *)scrollView:(TMLazyScrollView *)scrollView itemByMuiID:(NSString *)muiID
    根據(jù)muiID返回view。

TMLazyScrollView對外提供的方法有:

  • -reloadData() // 獲取數(shù)據(jù)后或者數(shù)據(jù)更新后加載全部數(shù)據(jù)
  • -setContentOffset: // 手動滑動lazyScrollView之后視圖的變化
reloadData方法的實現(xiàn)流程
  1. [self storeItemModelsFromIndex:0]
    清理所有本地數(shù)據(jù);將所有的view對應(yīng)的lazyModel存放進_modelBucket中,需要dataSource的方法1>和2>來提供lazyModel。

  2. -assembleSubviews:minY:maxY:
    統(tǒng)計當(dāng)前可見范圍內(nèi)的model的muiID集合newVisibleMuiIDs。

2.1 -recycleItems:newVisibleMuiIDs: (isReload參數(shù)為YES)
遍歷當(dāng)前的可視view數(shù)組,_visibleItems

2.1.1 如果其中的view的muiID在將要出現(xiàn)的muiIDs集合newVisibleMuiIDs中,那么這個view只需要刷新其中的數(shù)據(jù)即可,將其muiID加入_needReloadingMuiIDs中。
2.1.2 如果其中的view的muiID不在將要出現(xiàn)的muiIDs集合newVisibleMuiIDs中,那么這個view就需要離開屏幕

2.1.2.1 此時需要調(diào)用view的mui_didLeave()方法
2.1.2.2 如果此view的reuseIdentifier.length>0,也就是說這個view是需要被重用的,那么就將其隱藏,并且加入reusePool中,將此element從_visibleItems數(shù)組中移除掉。
2.1.2.3 如果此itemView不打算重用,將其muiID加入_needReloadingMuiIDs中。

2.2 -generateItems: (isReload參數(shù)為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 調(diào)用view的方法mui_afterGetView
2.2.1.1.2 設(shè)置view的muiID,并設(shè)置hidden為NO,就是要讓其顯示出來,
2.2.1.1.3 如果該view之前沒有顯示在屏幕上,那么這次需要將其加入_visibleItems數(shù)組中
2.2.1.1.3 將其重_needReloadingMuiIDs中刪除

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

2.2.2 將muiID從_newVisibleMuiIDs總移除,如果_newVisibleMuiIDs不為空,則繼續(xù)調(diào)用generateItems

reloadData流程中的-recycleItems:newVisibleMuiIDs:的處理流程可以用下圖來表示:
reloadData流程中的-recycleItems:newVisibleMuiIDs:
reloadData流程中的-generateItems:的處理流程可以用下圖來表示:
reloadData流程中的-generateItems:
setConentOffset方法的實現(xiàn)流程
  1. 用_lastContentOffset記錄了上次滑動的contentOffset,當(dāng)當(dāng)前contentOffse.y和_lastContentOffset.y的差值絕對值大于LazyBufferHeight時,更新_lastContentOffset為當(dāng)前的contentOffset,并調(diào)用assembleSubviews

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

setConentOffset流程中的-recycleItems:newVisibleMuiIDs:的處理流程可以用下圖來表示:
setConentOffset流程中的-recycleItems:newVisibleMuiIDs:
setConentOffset和reloadData的區(qū)別

區(qū)別在于recycleItem:newVisibleMuiIDs:中的第一個參數(shù)isReload,手動滑動isReload為NO,reloadData中的isReload為YES。

  • 如果_visibleItems中的element還要呆在屏幕上:
    1> 對于reloadData,仍然需要刷新其中內(nèi)容 (加入_needReloadingMuiIDs數(shù)組中)
    2> 對于手動滑動,不需要刷新
  • 如果_visibleItems中的element不需要呆在屏幕上
    1> 對于reloadData,仍然需要刷新其中內(nèi)容 (加入_needReloadingMuiIDs數(shù)組中)
    2> 對于手動滑動,不需要刷新
TMLazyScrollView通過self.dataSource獲取view

具體調(diào)用流程為:
1 - (UIView *)scrollView:(TMLazyScrollView *)scrollView itemByMuiID:(NSString *)muiID
該方法是TangramView實現(xiàn)TMLazyScrollView的dataSource的3>代理方法,TangramView在實現(xiàn)中會調(diào)用VC實現(xiàn)的返回itemView代理方法

2 - (UIView *)itemInTangramView:(TangramView *)view withModel:(NSObject<TangramItemModelProtocol> *)model forLayout:(UIView<TangramLayoutProtocol> *)layout atIndex:(NSUInteger)index
這是VC需要實現(xiàn)的代理方法。在其中,可能會調(diào)用dequeueReusableItemWithIdentifier:方法, 調(diào)用的是TMLazyScrollView的方法dequeueReusableItemWithIdentifier:(muiID:)

2.1 - (UIView *)dequeueReusableItemWithIdentifier:(NSString *)identifier muiID:(NSString *)muiID
如果當(dāng)前需要的(newVisibleMuiIDs中的muiID)view在屏幕上,則直接使用該view;
如果不在屏幕上,則從reusepool中拿一個,返回之;
如果什么也沒有,則直接返回nil。

2.2 如果dequeueReusableItemWithIdentifier:返回結(jié)果非空,則用model去刷新該element。
reuseableView = [TangramDefaultDataSourceHelper refreshElement:reuseableView byModel:model layout:layout tangramBus:self.tangramBus];

2.3 如果dequeueReusableItemWithIdentifier:返回結(jié)果為空,則需要創(chuàng)建一個element,并用model數(shù)據(jù)去填充該element。
reuseableView = [TangramDefaultDataSourceHelper elementByModel:model layout:layout tangramBus:self.tangramBus];

從這個角度來看,TMLazyScrollView還需要實現(xiàn)一個重用view的方法,dequeueReusableItemWithIdentifier:(muiID:)

dequeueReusableItemWithIdentifier:(muiID:)方法實現(xiàn)流程
  1. 通過identifier從reusepool中查找view
    1.1 如果找到,調(diào)用view的prepareForReuse
  2. 返回找到的view或者nil

總結(jié)

模型:

  • TMLazyItemModel
  • TMLazyModelBucket

控制器:

  • TMLazyScrollView
屬性:
  • TMLazyModelBucket *_modelBucket;
  • NSMutableSet<UIView *> *_visibleItems;
  • NSMutableSet<NSString *> _newVisibleMuiIDs;
  • TMLazyReusePool *reusePool;
  • id<TMLazyScrollViewDataSource> dataSource;
方法:
  • reloadData
  • setConentOffset
  • dequeueReusableItemWithIdentifier
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容