国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > iOS > 正文

詳解iOS 滾動(dòng)視圖的復(fù)用問(wèn)題解決方案

2020-07-26 02:59:59
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

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將要顯示的位置

主站蜘蛛池模板: 开鲁县| 资阳市| 德江县| 平湖市| 财经| 清涧县| 澄城县| 阿图什市| 伊吾县| 呼伦贝尔市| 郧西县| 嵩明县| 子洲县| 顺昌县| 大同市| 吉水县| 苗栗县| 沾化县| 汝阳县| 马公市| 舟山市| 新竹市| 抚远县| 汽车| 清苑县| 会昌县| 巍山| 贵溪市| 马山县| 梁平县| 东莞市| 凌源市| 白河县| 龙岩市| 富源县| 晋州市| 郯城县| 汝城县| 吴川市| 兴宁市| 莱阳市|