转载

为UITableView添加下拉刷新和加载更多功能

最近为公司项目中添加自动加载更多功能,于是将公司里下拉刷新和加载更多功能单独抽出来,放在UITableView的category中,可以做到无侵入添加这些功能,使得很多老代码只需要几句代码,即可实现这些功能。具体代码及使用方法:UITableView+PullData,其实实现逻辑很简单,基本看完代码就能完全理解,但是在实现的时候,遇到了一些问题,下面内容主要讲讲我是如何去解决这些问题的。

首先,公司之前代码就实现了下拉刷新和上拉加载更多功能,是通过继承基类UITableView,在子类中添加属性enablePullDataRefresh、enablePullDataLoadMore、pullDataDelegate、pullDataTableViewIsRefreshing、pullDataTableViewIsLoadingMore及代理方法去实现,具体的实现和我的代码基本一致,用子类的方式不是很优雅,每个想实现这个功能都需要用子类创建实例。如果用category的方式,就可以做到完全不用改变原有代码,只需要设置tableview的属性值,就可以实现功能,这里用的老思路,也没有遇到大问题,只有一些小问题,简单优化了下。

自动加载更多

主要的坑在自动加载更多上,自动加载更多就是在滑动UI TableView的时候,当快到底部时,提前自己去发起加载后面数据的请求并添加到数据源上,让用户没有感知的情况加载了新数据。

UITableViewDelegate:scrollViewDidScroll方法

可以想到要提前加载数据,什么时候发起请求呢?通过什么方式去获取条件是否满足呢?预加载与智能预加载(iOS)这篇博客讲了一种Threshold方式,可以通过Threshold值来确定触发条件,由scrollViewDidScroll去获取条件。于是很快就实现了自动加载功能,但是会有一个问题,每个想实现自动加载更多功能,都需要实现UITableView的scrollViewDidScroll代理方法,连代理方法里的代码都几乎一样,这样的实现方式不是太好看。

UITableView:KVO方法

既然把触发自动加载更多的条件及监测方式放在UITableView的代理上,不是很好,换个方式实现吧。scrollViewDidScroll代理方法都会伴随着contentOffset发生变化,我们可以通过监听contentOffset的值来判断条件是否成立,当条件满足的时候让tableView加载更多数据,自然会想到用KVO观察contentOffset的值发生变化,然后一系列判断是否满足条件。为了无侵入的设置自动加载更多,让tableview自己观察自己的contentOffset,当loadMoreStyle = TableViewLoadMoreStyleAuto时添加KVO观察,loadMoreStyle = TableViewLoadMoreStyleDefault时移除KVO观察。那么问题就来了,如果子类tableView自己也使用KVO会导致父类里的不执行,好吧,我想到如果要继承UITableView,就需要在+(void)load里去hook这个- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context方法,我用宏定义去帮助如何去hook,只需要在继承的时候写上方法+ (void)load {AutoLoadMoreMacro};,问题好像是解决了,但是问题又来了,大神说KVO不可以多次移除观察者等问题,如果我多次设置loadMoreStyle = TableViewLoadMoreStyleDefault就会出现问题,不得已又放弃KVO这种方式。

UITableView:setContentOffset方法

既然知道contentOffset值会变,KVO观察不可以,索性直接hook setContentOffset方法,在里面去判断是否满足自动加载更多条件,判断条件就是通过contentOffset.y和contentSize.height值做比较,当差值小到一个值,去执行加载更多,这样基本实现了自动加载,终于可以舒口气了,但是又发现一个问题,每次加载的时候都会触发两次,检查各个条件都是正确的,setContentOffset也是主线程里执行,怀疑可能setContentOffset里contentOffset.y和contentSize.height值做比较这种方式会有抖动,于是又要换个方式,直接拿数据源比较吧,tableView可以获取numberOfRowInSection,还有indexPathsForVisibleRows属性,可以通过比较可见的row和总row值比较来确定触发条件,但是这么做,只能让tableview只有一个section。

结束

以上就是我做自动加载更多遇到的问题,希望大家可以多多交流,发现一些不足,以及给予一些更好的实现方式。

作者:TalkingJourney

链接:https://juejin.im/post/5a310b055188252ae93af141

正文到此结束
Loading...