美文网首页 ios零碎记录ios开发整理iOS Developer
iOS 高性能异构滚动视图-LazyScrollView实现

iOS 高性能异构滚动视图-LazyScrollView实现

作者: 让历史重演 | 来源:发表于2016-12-08 10:00 被阅读647次

    LazyScrollView简介

    LazyScrollView 继承自ScrollView,目标是解决异构(与TableView的同构对比)滚动视图的复用回收问题。它可以支持跨View层的复用,用易用方式来生成一个高性能的滚动视图。此方案最先在天猫iOS客户端的首页落地。

    ----苹果核 - iOS 高性能异构滚动视图构建方案 —— LazyScrollView

    这篇文章中,博主详细介绍了LazyScrollView的使用和实现方案,但是并没有给出具体DEMO,这里只是站在巨人的肩膀上,给一个DEMO,同时也希望可以抛砖引玉。

    LazyScrollView使用

    暂时的实现比较简陋,目前只有一个id<LazyScrollViewDataSource> dataSource;,需要实现下面三个接口:

    @protocol LazyScrollViewDataSource <NSObject>
    @required
    // ScrollView一共展示多少个item
    - (NSUInteger)numberOfItemInScrollView:(LazyScrollView *)scrollView;
    // 要求根据index直接返回RectModel
    - (LSVRectModel *)scrollView:(LazyScrollView *)scrollView rectModelAtIndex:(NSUInteger)index;
    // 返回下标所对应的view
    - (UIView *)scrollView:(LazyScrollView *)scrollView itemByLsvId:(NSString *)lsvId;
    @end
    

    其中LSVRectModel就是原文中的TMMuiRectModel

    @interface LSVRectModel : NSObject
    // 转换后的绝对值rect
    @property (nonatomic, assign) CGRect absRect;
    // 业务下标
    @property (nonatomic, copy) NSString *lsvId;
    + (instancetype)modelWithRect:(CGRect)rect lsvId:(NSString *)lsvId;
    @end
    

    三个接口都很简单,和UITableView很类似,如果有不清楚,可以在底部查看DEMO或者原文。

    另外,LazyScrollView提供了三个接口,也都是仿照UITableView来的,所以整个LazyScrollView的使用应该是很容易上手的:

    - (void)reloadData;
    - (UIView *)dequeueReusableItemWithIdentifier:(NSString *)identifier;
    - (void)registerClass:(Class)viewClass forViewReuseIdentifier:(NSString *)identifier;
    

    LazyScrollView实现

    最主要的思路就是复用,所以有两个View池:

    @property (nonatomic, strong) NSMutableDictionary<NSString *, NSMutableSet *> *reuseViews;
    @property (nonatomic, strong) NSMutableSet<__kindof UIView *> *visibleViews;
    

    由于每个View可能对应不同的identifier,所以reuseViews是一个NSMutableDictionary
    当一个View滑出可见区域之后,会将它先从visibleViews中移除,然后添加到reuseViews中,并从LazyScrollViewremove,即调用removeFromSuperview。这个地方在原文中作者的表述可能让大家误会了。

    LazyScrollView中有一个Dictionary,key是reuseIdentifier,Value是对应reuseIdentifier被回收的View,当LazyScrollView得知这个View不该再出现了,会把View放在这里,并且把这个View hidden掉。

    这里作者用的是hidden掉,但是我们知道,hidden只是控制显隐,View本身还是在那里,也无法去复用。

    而当一个View滑到可见区域内时,需要先从reuseViews中复用,如果reuseViews没有,则重新创建一个。相关实现请看- (UIView *)dequeueReusableItemWithIdentifier:(NSString *)identifier;

    最后一个问题就是如何判断一个View是在可见区域内的。这里原文中说的很清晰,还有图片配合。建议大家还是移步原文。这里我简单说一下,找到顶边大于contentOffset.y - BUFFER_HEIGHT,底边小于contentOffset.y+CGRectGetHeight(self.bounds) + BUFFER_HEIGHT,然后两个集合取交集就是需要显示的View集合了。
    当然,这里有一些处理算法:

    • 顶边 做升序处理得到一个集合,对 底边 降序处理得到一个集合。
    • 采用二分法查找合适的位置,然后再对上一步得到的集合取子集即可。

    好了,说了这么多,先放出DEMO地址吧,希望大家可以帮助完善,也希望可以给个Star。
    https://github.com/HistoryZhang/LazyScrollView
    原文地址:苹果核 - iOS 高性能异构滚动视图构建方案 —— LazyScrollView(里面还有很多干货)。

    最后说一下目前写的几个问题,希望大家可以一起来优化:

    1. 没有处理View点击事件,即没有写delegate回调。
    2. 二分法查找合适位置的时候算法待优化。
    3. 从旧的visibleViews中移除被滑出的View算法待优化。

    贴一段第二个问题的代码:

    - (NSMutableSet *)findSetWithMinEdge:(CGFloat)minEdge {
        NSArray *ascendingEdgeArray =
        [self.allRects sortedArrayUsingComparator:^NSComparisonResult(LSVRectModel *obj1, LSVRectModel *obj2) {
            return CGRectGetMinY(obj1.absRect) > CGRectGetMinY(obj2.absRect) ? NSOrderedDescending : NSOrderedAscending;
        }];
        
        // TOOD: 此处待优化
        // 二分法
        NSInteger minIndex = 0;
        NSInteger maxIndex = ascendingEdgeArray.count - 1;
        NSInteger midIndex = (minIndex + maxIndex) / 2;
        LSVRectModel *model = ascendingEdgeArray[midIndex];
        while (minIndex < maxIndex - 1) {
            if (CGRectGetMinY(model.absRect) > minEdge) {
                maxIndex = midIndex;
            }
            else {
                minIndex = midIndex;
            }
            midIndex = (minIndex + maxIndex) / 2;
            model = ascendingEdgeArray[midIndex];
        }
        midIndex = MAX(midIndex - 1, 0);
        NSArray *array = [ascendingEdgeArray subarrayWithRange:NSMakeRange(midIndex, ascendingEdgeArray.count - midIndex)];
        return [NSMutableSet setWithArray:array];
    }
    

    再贴一段第三个问题的代码:

        NSMutableArray *newVisibleViews = [self visiableViewModels].mutableCopy;
        NSMutableArray *newVisibleLsvIds = [newVisibleViews valueForKey:@"lsvId"];
        NSMutableArray *removeViews = [NSMutableArray array];
        for (UIView *view in self.visibleViews) {
            if (![newVisibleLsvIds containsObject:view.lsvId]) {
                [removeViews addObject:view];
            }
        }
        for (UIView *view in removeViews) {
            [self.visibleViews removeObject:view];
            [self enqueueReusableView:view];
            [view removeFromSuperview];
        }
    
    扫码关注公众号

    原文链接

    <推广> 综合计算器 是一款多功能计算器,可以计算房贷、个税、年终奖、利息、BMI指数以及大小金额的计算器,再也不用下载多个计算器找来找去了。还支持实时更新贷款利率哦~。

    相关文章

      网友评论

        本文标题:iOS 高性能异构滚动视图-LazyScrollView实现

        本文链接:https://www.haomeiwen.com/subject/fgwjmttx.html