LazyScroll是什么
LazyScrollView 繼承自ScrollView,目標(biāo)是解決異構(gòu)(與TableView的同構(gòu)對(duì)比)滾動(dòng)視圖的復(fù)用回收問(wèn)題。它可以支持跨View層的復(fù)用,用易用方式來(lái)生成一個(gè)高性能的滾動(dòng)視圖。
為什么要用LazyScrollView
我們?cè)谧鍪醉?yè)的時(shí)候,往往展示的東西會(huì)很多,隨著View數(shù)量逐漸膨脹,沒(méi)有一套復(fù)用回收機(jī)制的ScrollView已經(jīng)影響到性能了,迫切需要處理對(duì)ScrollView中View的復(fù)用和回收。使用TableView只能用來(lái)解決同類(lèi)Cell的展示,然而在實(shí)際的場(chǎng)景中在ScrollView里面,View的種類(lèi)往往會(huì)比較多,所以使用TableView不適合我們的場(chǎng)景。
而UICollectionView本身的布局和復(fù)用回收機(jī)制不夠靈活,用起來(lái)也較為繁瑣。所以誕生了LazyScrollView去解決這個(gè)問(wèn)題。這也是天貓iOS客戶(hù)端的首頁(yè)落地方案。
LazyScroll使用
LazyScrollView的使用和TableView很像,不過(guò)多了一個(gè)需要實(shí)現(xiàn)的方法:返回對(duì)應(yīng)index的View 相對(duì)LazyScrollView的絕對(duì)坐標(biāo)。
實(shí)現(xiàn)LazyScrollViewDatasource
類(lèi)似TableView的用法,我們需要使用方實(shí)現(xiàn)LazyScrollViewDatasource的Delegate。
@protocol TMMuiLazyScrollViewDataSource <NSObject>@required//ScrollView展示item個(gè)數(shù)- (NSUInteger)numberOfItemInScrollView:(TMMuiLazyScrollView *)scrollView;//要求根據(jù)index直接返回RectModel- (TMMuiRectModel *)scrollView:(TMMuiLazyScrollView *)scrollView rectModelAtIndex:(NSUInteger)index;//返回下標(biāo)所對(duì)應(yīng)的view- (UIView *)scrollView:(TMMuiLazyScrollView *)scrollView itemByMuiID:(NSString *)muiID;
LazyScrollView的核心是在初始狀態(tài)就得知所有View應(yīng)該顯示的位置。第一個(gè)方法很簡(jiǎn)單,獲取LazyScrollView中item的個(gè)數(shù)。第二個(gè)方法需要按照Index返回TMMuiRectModel ,它會(huì)攜帶對(duì)應(yīng)index的View 相對(duì)LazyScrollView的絕對(duì)坐標(biāo)。
這里出現(xiàn)了一個(gè)TMMuiRectModel ,這是個(gè)什么東西呢?我們看一下代碼:
@interface TMMuiRectModel:NSObject//轉(zhuǎn)換后的絕對(duì)值rect@property (nonatomic,assign) CGRect absRect;//業(yè)務(wù)下標(biāo)@property (nonatomic,copy) NSString *muiID;
這里有兩個(gè)屬性,absRect是LazyScroll中的View相對(duì)LazyScrollView的絕對(duì)坐標(biāo),muiID是這個(gè)View在LazyScrollView中唯一的標(biāo)識(shí)符,可賦值也可不賦值。
第三個(gè)方法,返回View。
@interface UIView(TMMui)
//索引過(guò)的標(biāo)識(shí),在LazyScrollView范圍內(nèi)唯一@property (nonatomic, copy) NSString *muiID;//重用的ID@property (nonatomic, copy) NSString *reuseIdentifier;
首先,我們?cè)赨IView之外加了一個(gè)Category,這個(gè)category可以讓View攜帶muiID和reuseIdentifier,對(duì)于返回的View來(lái)說(shuō),只需要在乎對(duì)View的reuseIdentifier賦值,muiID的賦值會(huì)在lazyScrollView中處理掉。reuseIdentifier相同的View會(huì)被復(fù)用,如果這個(gè)View的reuseIdentifier是nil或者空字符串,則不會(huì)被復(fù)用。
LazyScrollView內(nèi)部原理分析
首先來(lái)看一個(gè)簡(jiǎn)單的案例:
根據(jù)DataSource獲取所有的TMMuiRectModel
根據(jù)DataSource的Delegate,拿到所有的View應(yīng)該被顯示的位置。這一步,核心是拿到的位置是確定的。根據(jù)Demo,我們觀察從 0/1 - 2/3 之間這些View,這個(gè)時(shí)候LazyScrollView拿到的Rect如下:
Index | 標(biāo)號(hào)(MUIID) | Rect |
---|---|---|
0 | 0/0 | origin = (x = 25, y = 15), size = (width = 156, height = 150 |
1 | 0/1 | origin = (x = 194, y = 15), size = (width = 156, height = 150) |
2 | 0/2 | origin = (x = 25, y = 180), size = (width = 156, height = 150) |
3 | 0/3 | origin = (x = 194, y = 180), size = (width = 156, height = 150 |
4 | 1/0 | origin = (x = 5, y = 360), size = (width = 177.5, height = 150) |
5 | 1/1 | origin = (x = 192.5, y = 426), size = (width = 84, height = 84) |
6 | 1/2 | origin = (x = 192.5, y = 360), size = (width = 177.5, height = 56) |
7 | 1/3 | origin = (x = 286.5, y = 426), size = (width = 83.5, height = 84) |
8 | 2/0 | origin = (x = 25, y = 530), size = (width = 325, height = 150) |
9 | 2/1 | origin = (x = 25, y = 695), size = (width = 325, height = 150) |
10 | 2/2 | origin = (x = 25, y = 860), size = (width = 325, height = 150) |
排序
拿到了這些位置之后,接下來(lái)做的事情就是排序。排序生成的索引會(huì)有兩個(gè):根據(jù)頂邊(y)升序排序的索引和根據(jù)底邊(y+height)降序排序的索引。
根據(jù)頂邊(y)升序排序的索引
Index | 標(biāo)號(hào)(MUIID) | Rect |
---|---|---|
0 | 0/0 | origin = (x = 25, y = 15), size = (width = 156, height = 150 |
1 | 0/1 | origin = (x = 194, y = 15), size = (width = 156, height = 150) |
2 | 0/2 | origin = (x = 25, y = 180), size = (width = 156, height = 150) |
3 | 0/3 | origin = (x = 194, y = 180), size = (width = 156, height = 150 |
4 | 1/0 | origin = (x = 5, y = 360), size = (width = 177.5, height = 150) |
5 | 1/1 | origin = (x = 192.5, y = 360), size = (width = 177.5, height = 56) |
6 | 1/2 | origin = (x = 192.5, y = 360), size = (width = 177.5, height = 56) |
7 | 1/3 | origin = (x = 286.5, y = 426), size = (width = 83.5, height = 84) |
8 | 2/0 | origin = (x = 25, y = 530), size = (width = 325, height = 150) |
9 | 2/1 | origin = (x = 25, y = 695), size = (width = 325, height = 150) |
10 | 2/2 | origin = (x = 25, y = 860), size = (width = 325, height = 150) |
根據(jù)底邊(y+height)降序排序的索引
Index | 標(biāo)號(hào)(MUIID) | Rect |
---|---|---|
0 | 2/2 | origin = (x = 25, y = 860), size = (width = 325, height = 150) |
1 | 2/1 | origin = (x = 25, y = 695), size = (width = 325, height = 150) |
2 | 2/0 | origin = (x = 25, y = 530), size = (width = 325, height = 150) |
3 | 1/0 | origin = (x = 5, y = 360), size = (width = 177.5, height = 150) |
4 | 1/2 | origin = (x = 192.5, y = 360), size = (width = 177.5, height = 56) |
5 | 1/3 | origin = (x = 286.5, y = 426), size = (width = 83.5, height = 84) |
6 | 1/1 | origin = (x = 192.5, y = 426), size = (width = 84, height = 84) |
7 | 0/2 | origin = (x = 25, y = 180), size = (width = 156, height = 150) |
8 | 0/3 | origin = (x = 194, y = 180), size = (width = 156, height = 150 |
9 | 0/0 | origin = (x = 25, y = 15), size = (width = 156, height = 150 |
10 | 0/1 | origin = (x = 194, y = 15), size = (width = 156, height = 150) |
查找
前兩步是在執(zhí)行完reload,在視圖還沒(méi)有生成的時(shí)候就開(kāi)始做了,而接下來(lái)的步驟在要生成視圖(初始化或滾動(dòng)的時(shí)候)才會(huì)去做。
我們?cè)O(shè)定了Buffer為上下各20,滾動(dòng)超過(guò)20個(gè)像素后才會(huì)指定查找視圖并顯示的動(dòng)作。舉個(gè)例子,如下圖,紅圈是應(yīng)該顯示的區(qū)域。
如上圖所示,現(xiàn)在已知的是紅圈頂邊y是242,底邊y是949,加上緩沖區(qū)Buffer,應(yīng)該是找222 - 969 之間的View。我們要做的是,找到底邊y小于969的Model和頂邊y大于222的Model,取交集,就是我們要顯示的View。
采用的方法為二分查找,在根據(jù)頂邊升序排序的索引中找949,找到的index為0(MUIID為2/2),我們使用一個(gè)Set,把根據(jù)頂邊排序中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。
根據(jù)底邊排序的索引中找222,找到的index為2,我們把index >= 2的元素放在另一個(gè)Set,獲取的Set中包含的muiID為0/2,0/3,1/0,1/1,1/2,1/3,2/0,2/1,2/2
兩個(gè)Set取交集,得到的就是我們的ResultSet,這里面都是我們要顯示View的Model,它們的muiID是0/2,0/3,1/0,1/1,1/2,1/3,2/0,2/1,2/2。
回收、復(fù)用、生成
我們知道了應(yīng)該顯示哪些View,但是我們之后做的第一步是把不需要顯示的View加入到復(fù)用池中。LazyScroll可以取到當(dāng)前顯示了的View,拿當(dāng)前顯示的View的muiID和將要顯示view的Model的muiID做對(duì)比,可以知道當(dāng)前顯示的View哪些應(yīng)該被回收。
LazyScrollView中有一個(gè)Dictionary,key是reuseIdentifier,Value是對(duì)應(yīng)reuseIdentifier被回收的View,當(dāng)LazyScrollView得知這個(gè)View不該再出現(xiàn)了,會(huì)把View放在這里,并且把這個(gè)View hidden掉。
然后,用LazyScrollView會(huì)去調(diào)用datasource。
- (UIView *)scrollView:(TMMuiLazyScrollView *)scrollView itemByMuiID:(NSString *)muiID;
復(fù)用還是不復(fù)用,是由datasource決定的。如果要復(fù)用,需要datasource方法內(nèi)調(diào)用,即:
- (UIView *)dequeueReusableItemWithIdentifier:(NSString *)identifier
獲取復(fù)用的View,這個(gè)方法取出來(lái)的View就是在上一段所說(shuō)的Dictionary中拿的。
最后我們看一下LazyScrollView的使用流程:找到所有View將要顯示的位置
主站蜘蛛池模板:
开鲁县|
资阳市|
德江县|
平湖市|
财经|
清涧县|
澄城县|
阿图什市|
伊吾县|
呼伦贝尔市|
郧西县|
嵩明县|
子洲县|
顺昌县|
大同市|
吉水县|
苗栗县|
沾化县|
汝阳县|
马公市|
舟山市|
新竹市|
抚远县|
汽车|
清苑县|
会昌县|
巍山|
贵溪市|
马山县|
梁平县|
东莞市|
凌源市|
白河县|
龙岩市|
富源县|
晋州市|
郯城县|
汝城县|
吴川市|
兴宁市|
莱阳市|