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)流程
[self storeItemModelsFromIndex:0]
清理所有本地數(shù)據(jù);將所有的view對應(yīng)的lazyModel存放進_modelBucket中,需要dataSource的方法1>和2>來提供lazyModel。-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流程中的-generateItems:的處理流程可以用下圖來表示:

setConentOffset方法的實現(xiàn)流程
用_lastContentOffset記錄了上次滑動的contentOffset,當(dāng)當(dāng)前contentOffse.y和_lastContentOffset.y的差值絕對值大于LazyBufferHeight時,更新_lastContentOffset為當(dāng)前的contentOffset,并調(diào)用assembleSubviews
-assembleSubviews:minY:maxY:
剩余流程同reloadData。
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)流程
- 通過identifier從reusepool中查找view
1.1 如果找到,調(diào)用view的prepareForReuse - 返回找到的view或者nil
總結(jié)
模型:
- TMLazyItemModel
- TMLazyModelBucket
控制器:
- TMLazyScrollView
屬性:
- TMLazyModelBucket *_modelBucket;
- NSMutableSet<UIView *> *_visibleItems;
- NSMutableSet<NSString *> _newVisibleMuiIDs;
- TMLazyReusePool *reusePool;
- id<TMLazyScrollViewDataSource> dataSource;
方法:
- reloadData
- setConentOffset
- dequeueReusableItemWithIdentifier