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如下:
排序
拿到了這些位置之後,接下來做的事情就是排序。排序生成的索引會有兩個:根據頂邊(y)升序排序的索引和根據底邊(y+height)降序排序的索引。
根據頂邊(y)升序排序的索引
根據底邊(y+height)降序排序的索引
查找
前兩步是在執行完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 – 回收 – 創建/復用。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。