LazyScrollView的特殊使用情况

之前做一个UITableView流水布局的需求的时候歪打正着的看到了这个库,使我免去了重复造轮子的窘境。

首先我做了一个兜底方案是直接算出所有的frame,直接往一个大Cell上添加。当然,这种方案在数据源变化的情况下有点恶心。

自然而然我就想到了我自己的带缓存池的库-ZCHScrollChannelView。后面由于惯性思维跳了一个坑,这个后面再说。

我们组的安卓哥们给我推荐了一下阿里巴巴的Tangram库,这个库本身可以分为iOS和安卓两个库。然后我在研究这个库的时候发现它的缓存池布局控件是LazyScrollView,当然,也支持pod集成。

///PS,安装这个CocoaPods库会带入一个天猫的UI分类操作的库进来:`TMUtils`,
///不过可以放心的是这两个库都没有进行任何的网络请求,
///也就是说,这是一个离线库(本地库)
pod 'LazyScroll'

框架情况
在这里插入图片描述

试用的一些Demo有的情况我就不赘述了,对于我们度小满理财的这个项目来说,有点特殊因为基本上所有的cell,item,view都会经过一个类来解析后台的模板号,来确定使用哪个模板,而且这个流水布局需要放置到任何地方,所以,Demo里头的放置到footer上是不行的(放置到footer上是不停更新footer的高度来代替“刷新”)。

当外层有scrollView及其子类的时候,可以设置LazyScroll的outerScrollView的属性,LazyScroll内部会自动监听外部ScrollView的滚动,然后根据UIKit的方法换算LazyScroll这个控件的显示范围,如果大家有越狱设备,可以使用“Reveal”查看一下天猫首页的流水布局或者是淘宝首页的流水布局。它会有一个特别高的lazyScroll控件,屏幕内的控件显示,屏幕外的控件不会显示。

> 这里先留个空,回家截个图到这里来。
这就是这个流水布局的部分了.如果想看这个部件的contentOffset有多高的话,可以看一下下图

在这里插入图片描述

上面也说了,我们这里是有特殊情况的。cell不停更新高度的话就需要外层不停的reload,这样体验就特别差了。所以和我兜底方案也一样,每次提前算出cell的高度,高度就尽量少动,一次刷新tableview,内部就可以reload。

outerScrollView的设置

与MJRefresh需要监听父控件"UIScrollVIew及其子类"的滚动是一个道理,LazyScrollView也需要监听父控件的滚动来间接“获取”LazyScrollView应该的“可视区域”。具体怎么做的可以参考我这一篇文章MJRefresh研究

上面说了,我们这里是UITableViewCell,所以和Demo的outerScrollView的设置会有些不一样。

在这里插入图片描述

既然原理是一样(KVO监听父控件属性,具体可以戳这篇文章:MJRefresh研究),那么当视图生命周期结束,outerScrollView也得被移除(置为nil),下面是这个属性当说明。

/**
 LazyScrollView can be used as a subview of another ScrollView.
 For example:
 You can use LazyScrollView as footerView of TableView.
 Then the outerScrollView should be that TableView.
 You MUST set this property to nil before the outerScrollView's dealloc.
 */
@property (nonatomic, weak, nullable) UIScrollView *outerScrollView;

1.在cellforRow设置

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableView *cell = [tableView dequeueReusableCellWithIdentifier:@"标志符"];
    if (cell == nil) {
        cell = [[FlowLayoutCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:@"标志符"];
    }
    if ([cell isKindOfClass:[flowLayoutClass class]]) {
    	cell.flowLayoutView.outerScrollView = tableView;
    }
}

2.通过生命周期来设置

- (void)willMoveToSuperview:(UIView *)newSuperview {
    [super willMoveToSuperview:newSuperview];
    if ([newSuperview isKindOfClass:[UITableView class]]) {
        self.flowLayoutView.outerScrollView = newSuperview;
    }
}

这个2.方法是不是看起来更好一些,更没有侵入性一些。不过,这么写是有问题的。
- (void)willMoveToSuperview:(UIView *)newSuperview会在两个时机调用,一个是addSubView,将要把视图添加上去的时候。还有个一个是willMoveToSuperView,将要把视图移除除去的时候,区别是,add的时候newSuperview是有值的,移除的时候newSuperview的值是nil。

在iOS7-iOS11中。cell的superView并不直接是UITableView。而是被添加到了一个[id class]后得出的是UITableViewWrapperView类型的.通过下面的代码我们可以得知这个类是UIScollView的子类

UIView *wrapperView = [[NSClassFromString(@"UITableViewWrapperView") alloc] init];
if ([wrapperView isKindOfClass:[UIScrollView class]]) {
	NSLog(@"scrollView");
} else if ([wrapperView isKindOfClass:[UITableView class]]) {
	NSLog(@"tableView");
} else if ([wrapperView isKindOfClass:[UICollectionView class]]) {
	NSLog(@"collectionView");
}

所以这段代码可以改成:

- (void)willMoveToSuperview:(UIView *)newSuperview {
    [super willMoveToSuperview:newSuperview];
    if ([newSuperview isKindOfClass:[UITableView class]]) {
        self.flowLayoutView.outerScrollView = newSuperview;
    } else if ([newSuperview isKindOfClass:[NSClassFromString(@"UITableViewWrapperView")]]) {
		// 获取它的下一个响应者
		self.flowLayoutView.outerScrollView = newSuperview.nextResponder;
	}
}

3.第二种方法的改版

便利一下获取到[UITableView class]然后设置到outerScrollView上,因为我这个需求是已经确定UITableViewCell上了。所以可以这么干,不是非常通用的,例如上面提到的UIScrollView的子类UITableViewWrapperView

因为是Cell内做流水布局,所以尽量要将tableView的reloadData的次数减少来减少抖动。所以我的方案是在yy_modelWithJson:的时候就把布局计算完毕,需要多高直接在heightForRow的时候直接返回。然后就是简单的使用lazyScroll的datasource方法了。

额外的坑

有三点坑需要注意。1.autoAddSubview属性,这个属性需要设置为yes,不然reloadData的时候是不会显示视图的,这是我上面提到的惯性思维导致的坑的第一点。2.lazyScrollView调用reloadData之前需要调用clearVisibleItems:并且传入YES,不然屏幕上已经显示的View是不会消失的。

第三点我特意提出来讲,看得出来lazyScroll想要尽量的模仿系统的API。例如

/// TMLazyScrollView.h
/**
 Get reuseable item view by reuseIdentifier.
 */
- (nullable UIView *)dequeueReusableItemWithIdentifier:(nonnull NSString *)identifier;
/**
 Get reuseable item view by reuseIdentifier and muiID.
 MuiID has higher priority.
 */
- (nullable UIView *)dequeueReusableItemWithIdentifier:(nonnull NSString *)identifier
                                                 muiID:(nullable NSString *)muiID;

reuseIdentifier & muiID

// UIView + TMLazyScrollView
@property (nonatomic, copy) NSString *muiID;
@property (nonatomic, copy) NSString *reuseIdentifier;

- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier;
- (instancetype)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier;

坑点就是这个reuseIdentifier导致的,大家也知道,UITableViewCell与UICollectionViewCell都具有这个属性,如果想要通用性的话用UICollectionViewCell来当重用的UIView是会取不到控件的。因为reuseIdentifier取的话一直为nil。如果一定要用的话,我建议把这个库下载再来,把reuseIdentifier以及reuseIdentifier相关的代码加一个前缀来与系统的区分一下即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值