LazyScrollView 是一個高性能的 scrollView 重用布局框架, 實現(xiàn)了視圖的重用和自動加載, 值得學(xué)習(xí)一二
使用的時候的核心代碼分析
@protocol TMMuiLazyScrollViewDataSource <NSObject>
@required
- (NSUInteger)numberOfItemInScrollView:(nonnull TMMuiLazyScrollView *)scrollView; // 0 by default.
// Return the view model by spcial index.
- (nonnull TMMuiRectModel *)scrollView:(nonnull TMMuiLazyScrollView *)scrollView
rectModelAtIndex:(NSUInteger)index;
/**
* You should render the item view here. And the view is probably . Item view display. You should
* *always* try to reuse views by setting each view's reuseIdentifier and querying for available
* reusable views with dequeueReusableItemWithIdentifier:
*/
- (nullable UIView *)scrollView:(nonnull TMMuiLazyScrollView *)scrollView itemByMuiID:(nonnull NSString *)muiID;
@end
我們從上面這個代理中就可以大致得到一些信息
- scrollView 重用顯示是通過 delegate 來完成的
- 我們需要為每個 item 返回一個 TMMuiRectModel
- 我們需要通過 id 自行配置對應(yīng)的 view(在這里需要注意的是獲取 view 需要調(diào)用 scrollView 的方法)
那么 TMMuiRectModel 是個什么呢, 我們通過源碼知道它只是一個布局屬性, 不過未來要是能加上一些層級顯示的相關(guān)屬性就更好了, 因為視圖有可能疊加在一起
@interface TMMuiRectModel: NSObject
// A rect that relative to the scroll view.
@property (nonatomic, assign) CGRect absoluteRect;
// A uniq string that identify a model.
@property (nonatomic, copy, nonnull) NSString *muiID;
@end
那么看了這么多, 在沒有看更多代碼的情況下, 我們來猜想一下具體的實現(xiàn)方式吧
- 我們在加載視圖的時候, 會遍歷所有的 rect, 計算已經(jīng)顯示或者將要顯示的 id
- 在計算顯示的 id 的時候, 同時也要計算消失的 id
- 獲取顯示的 id 對應(yīng)的 view, 并且把他們放到 view 上
簡單的說流程也就是這樣了, 但是如何保證性能和正確性, 這個我們就要在更細(xì)致的學(xué)習(xí)源碼了
更細(xì)致的流程
- 我們要緩存所有的布局信息, 但是在面對 scrollView 滑動的方向不確定的情況下, lazy 有緩存了兩個方向的所有索引 modelsSortedByTop 和 modelsSortedByBottom, 這樣的話在滑動的時候我們就不需要每次全局遍歷了
- 第二部就是裝配所有的 cell
2.1 首先是得到一個顯示范圍, 然后得到這里面應(yīng)該顯示的 id 集合(通過上面的到的索引獲取, 這里使用的了二分法)
2.2 然后根據(jù) id 的集合處理目前已經(jīng)顯示的 view, 值得一提的是 view 的兩個附加 id, reuseIdentifier 是用來處理重用顯示的, muiID 是唯一 id, reuseIdentifier 為 空的 view 并不會做額外的處理操作
2.3 之后就要處理真正要顯示的 cell 了 -- 還未顯示的 cell 和 需要重載的 cell, 通過代理獲取對應(yīng)的 view, 并且返回就好 - lazy 做了一些統(tǒng)計操作, 能記錄 cell 的出現(xiàn)次數(shù), 原理是每次緩存上一次未出現(xiàn)的 cell id, 如果這一次出現(xiàn), time + 1
關(guān)于 cell, lazy 還寫了一個 協(xié)議來記錄處理 cell view
@protocol TMMuiLazyScrollViewCellProtocol<NSObject>
@optional
// if call dequeueReusableItemWithIdentifier to get a reuseable view,the same as "prepareForReuse" in UITableViewCell
- (void)mui_prepareForReuse;
// When view enter the visible area of LazyScrollView ,call this method.
// First 'times' is 0
- (void)mui_didEnterWithTimes:(NSUInteger)times;
// When we need render the view, call this method.
// The difference between this method and 'mui_didEnterWithTimes' is there is a buffer area in LazyScrollView(RenderBufferWindow), first we will call 'mui_afterGetView'.
- (void)mui_afterGetView;
@end
關(guān)于 性能問題, 不僅用到了索引, 還使用了刷新閾值, 在 scrollViewDidScroll 代理中移動超過閾值的時候才會刷新 UI, 檢查 布局
后記
我感覺還是有一些可以優(yōu)化的點的
- 視圖的層級優(yōu)先級問題
- 目前只支持 Y 軸的重用加載, 未來可以加入多個方向的
- 目前對重用還只是很初級的使用, 未來希望能有更加多樣化的 api
- 可以根據(jù)移動速度,方向進行預(yù)加載