LazyScrollView 是一个高性能的 scrollView 重用布局框架, 实现了视图的重用和自动加载, 值得学习一二
使用的时候的核心代码分析
@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 自行配置对应的 view(在这里需要注意的是获取 view 需要调用 scrollView 的方法)
那么 TMMuiRectModel 是个什么呢, 我们通过源码知道它只是一个布局属性, 不过未来要是能加上一些层级显示的相关属性就更好了, 因为视图有可能叠加在一起
@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
那么看了这么多, 在没有看更多代码的情况下, 我们来猜想一下具体的实现方式吧
- 我们在加载视图的时候, 会遍历所有的 rect, 计算已经显示或者将要显示的 id
- 在计算显示的 id 的时候, 同时也要计算消失的 id
- 获取显示的 id 对应的 view, 并且把他们放到 view 上
简单的说流程也就是这样了, 但是如何保证性能和正确性, 这个我们就要在更细致的学习源码了
更细致的流程
- 我们要缓存所有的布局信息, 但是在面对 scrollView 滑动的方向不确定的情况下, lazy 有缓存了两个方向的所有索引 modelsSortedByTop 和 modelsSortedByBottom, 这样的话在滑动的时候我们就不需要每次全局遍历了
- 第二部就是装配所有的 cell
2.1 首先是得到一个显示范围, 然后得到这里面应该显示的 id 集合(通过上面的到的索引获取, 这里使用的了二分法)
2.2 然后根据 id 的集合处理目前已经显示的 view, 值得一提的是 view 的两个附加 id, reuseIdentifier 是用来处理重用显示的, muiID 是唯一 id, reuseIdentifier 为 空的 view 并不会做额外的处理操作
2.3 之后就要处理真正要显示的 cell 了 -- 还未显示的 cell 和 需要重载的 cell, 通过代理获取对应的 view, 并且返回就好 - lazy 做了一些统计操作, 能记录 cell 的出现次数, 原理是每次缓存上一次未出现的 cell id, 如果这一次出现, time + 1
关于 cell, lazy 还写了一个 协议来记录处理 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
关于 性能问题, 不仅用到了索引, 还使用了刷新阈值, 在 scrollViewDidScroll 代理中移动超过阈值的时候才会刷新 UI, 检查 布局
后记
我感觉还是有一些可以优化的点的
- 视图的层级优先级问题
- 目前只支持 Y 轴的重用加载, 未来可以加入多个方向的
- 目前对重用还只是很初级的使用, 未来希望能有更加多样化的 api
- 可以根据移动速度,方向进行预加载