OC-抖音上拉加载(你以为单纯用MJRefresh就能实现?那你就错了)

2023-11-21 16:50

本文主要是介绍OC-抖音上拉加载(你以为单纯用MJRefresh就能实现?那你就错了),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

先上DEMO记得star哦

效果图

 

之前实现了抖音下拉刷新效果之后就没再继续研究,想着上拉加载随便集成一下MJRefresh就可以了,很简单嘛,等需要的时候再加进去就好了。
直到某一天有个小伙伴跟我说添加上拉加载有问题,我就不信了,怎么可能呢,自己试了试还真是不少坑。

现在想来之前真的是图样图森破,你以为你以为的就是你以为的?实践出真知,万事都不能想当然,只有自己真的去操作了才能明白。

下面咱们来一起实现一下:
看之前先熟悉一下OC-仿抖音下拉刷新,操作是基于之前的demo的

1、添加上拉加载-使用延时模拟请求

self.tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{[weakSelf getMoreData];});}];

加上mj_footer之后运行起来,发现根本触发不了mj,因为之前代码里默认关闭了tableView的bounces,滑到最后不打开弹性效果是没办法往上拖拽触发mj。

一开始的想法是判断当前是否播放到列表的最后一个cell,如果是就打开,不是就关闭。后来想想其实只有第一个cell的时候才需要关闭弹性效果,就改成了下面代码:

//此方法每次cell播放的时候都会掉用,所以写在了这里,实时判断
- (void)tableView:(UITableView *)tableView willPlayVideoOnCell:(UITableViewCell *)cell {VideoTableViewCell *Cell = (VideoTableViewCell *)cell;Cell.playButton.selected = NO;playIndex = (int)Cell.playButton.tag;[cell.jp_videoPlayView jp_resumeMutePlayWithURL:cell.jp_videoURLbufferingIndicator:nilprogressView:nilconfigurationCompletion:^(UIView * _Nonnull view, JPVideoPlayerModel * _Nonnull playerModel) {view.jp_muted = NO;}];if (Cell.playButton.tag==0) {//列表第一个cell时关闭self.tableView.bounces = NO;}elseself.tableView.bounces = YES;
}

2、pagingEnabled和mj的矛盾

按照第一步,调整bounces的开关后,确实能够触发mj了,但是由于pagingEnabled的回弹效果,每次上拉触发mj后页面又回滚回去,没有正常上拉加载时footer悬停然后转圈的效果,导致用户根本看不见mj的加载状态,并且加载完成后reloadData,cell的位置会错乱,不是刚好整屏整屏的显示,有偏移。

1)先解决下回滚的问题

既然pagingEnabled影响了加载效果的显示,那是不是也可以通过手段来控制pagingEnabled的开关呢?

于是我开始监控tableView的滑动,通过playIndex记录当前播放到第几个cell,当播放到最后一个cell,用户又继续上滑说明用户正在上拉加载,此时是关闭pagingEnabled的最佳时机,具体代码如下,有注释:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{[self.tableView jp_scrollViewDidScroll];int index= (int)self.tableView.contentOffset.y/kHeight;//scroll是与整屏相比的偏移量,肯定是正的float scroll = self.tableView.contentOffset.y- index*kHeight;//与上一个滑动点比较,区分上滑还是下滑float offset = self.tableView.contentOffset.y- oldOffset.y;//记录当前tableView.contentOffsetoldOffset = self.tableView.contentOffset;if (offset>0) {//上拉-44是mj_footer的高度,当拖拽超过44的时候会触发mjif (playIndex==_tableView.items.count-1&&scroll>44) {if (_updating==NO) {//判断是否正在刷新,正在刷新就不再进行如下设置,以免重复加载_updating = YES;//进到这里说明用户正在上拉加载,触发mj,此时要关闭翻页功能否则页面回弹mj_footer就看不到了,setContentOffset也无效self.tableView.pagingEnabled = NO;//给tableView设置一个固定的Offset,往上偏移点,将footer展示出来,要大于44才会触发footer[self.tableView setContentOffset:CGPointMake(0, index*kHeight+50) animated:NO];[self.tableView.mj_footer beginRefreshing];} }}
}

 

 

然后在加载结束后再打开pagingEnabled即可,这样就能看到mj的加载状态了,抖音加载结束后会自动滚动到下一个cell播放,所以我也做了此操作,但是自动滚动后就会发现当前cell总是向上偏移了一部分,露出当前cell的下一个cell,如图:

红框内偏移高度

2)解决偏移问题

我打印了cell自动滚动后self.tableView.contentOffset.y,与整屏偏移量是44,也就是说页面刚好上移了44像素,那不就是footer的高度吗?于是我在自动滚动后让tableView.contentOffset.y再下移44,矫正过来,发现好使,代码如下:

-(void)getMoreData
{_updating=NO;[self.tableView.mj_footer endRefreshing];int index = (int)self.pathStrings.count;[self.pathStrings addObjectsFromArray:@[@"http://p11s9kqxf.bkt.clouddn.com/coder.mp4",@"http://p11s9kqxf.bkt.clouddn.com/cat.mp4",@"http://p11s9kqxf.bkt.clouddn.com/coder.mp4",@"http://p11s9kqxf.bkt.clouddn.com/cat.mp4"]];[self.tableView reloadData];//滚动到下一个cell[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:NO];NSLog(@"1w:%.f",self.tableView.contentOffset.y);//mjfooter高度是44,上拉加载时页面会向上偏移44像素,数据加载完毕后需要将contentOffset复位self.tableView.contentOffset =CGPointMake(0, self.tableView.contentOffset.y-44);NSLog(@"%.f",self.tableView.contentOffset.y);//让cell开始播放VideoTableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];[self tableView:self.tableView willPlayVideoOnCell:cell];//刷新结束,开启翻页功能self.tableView.pagingEnabled = YES;
}

3、ios11适配问题

 

 

根据上面,小偏移问题解决了,可是我继续再上滑查看后面cell的时候,诡异的事情发生了,后面的偏移更大了,而且滑动到底部就再也拽不动了:

红框内偏移部分有点大

这就有点傻眼了,这偏移的过分了点,我轻轻滑动页面,打印tableView减速后contentOffset.y的数值,发现数值没问题,刚好是屏幕高度的整数倍,一点都没偏移。

这是什么情况?翻页效果、回弹效果都没问题,contentOffset.y也对着呢,就是cell显示的不正常,难道是pagingEnabled在反复开关、设置contentOffset.y之后有点神经错乱了?

于是我尝试了各种办法,彻底关闭pagingEnabled正常滑动cell、修改cell的高度不让它全屏在进行测试、通过监控滑动过程自己模拟翻页效果、翻看老项目非全屏cell滑动加载效果……所有测试失败后我又回退到一开始的样子

啊啊啊啊,总之我折腾了一天,感觉要疯的节奏,眼看快下班了,要不真机试试吧,说不定是模拟器有问题呢?(当时我的手机是11.3,刚升级,xcode是9.2不支持,升级xcode太费时间了,所以一直在用模拟器测试)

从同事那借了个低版本的手机,10.3的,运行后,完美,拖拽,刷新,加载,复位一点问题都没有,很顺畅。(此时的我心中一万个草泥马奔腾而过……模拟器的锅,让我浪费了一天)

没问题了,就开心的下班了。

第二天到了公司,想着要不给xcode升个级吧,总借手机用怪麻烦的,然后是漫长的等待升级。升级结束后,把程序在我手机上运行了一下,发现昨天的诡异问题又出现了。就隔了一个晚上,怎么就不好使了?谁动我代码了?

又跑去同事那借了手机运行,没问题,在我手机上再次运行,有问题。同样的代码,设备一样,效果却不一样。唯一的差异在于系统,我是11.3,他是10.3。等等,难道是ios11的适配没做好?

翻了翻代码,果然没适配,于是把之前适配ios11的代码搬过来,一运行,彻底没问题了。

        self.estimatedRowHeight = 0;self.estimatedSectionHeaderHeight = 0;self.estimatedSectionFooterHeight = 0;//适配ios11自适应上导航 安全区域self.separatorStyle = UITableViewCellSeparatorStyleNone;SEL selector = NSSelectorFromString(@"setContentInsetAdjustmentBehavior:");if ([self respondsToSelector:selector]) {IMP imp = [self methodForSelector:selector];void (*func)(id, SEL, NSInteger) = (void *)imp;func(self, selector, 2);}

适配ios11的代码有那么多行,其实有效果的是estimatedRowHeight这个。具体请参考链接:关于iOS11中estimatedRowHeight看完你就明白了

4、细节整理

确定没问题了之后,我将代码整理了一番,整理好之后又测试了一遍,将延时时间调大了观察效果,当页面停留在加载状态的时候我手指下滑了一下,发现没有翻页回弹的效果了,一下子下滑了好几个cell。

哦,对了,我在加载的时候关闭了pagingEnabled,得在加载结束后才会打开,可是此时如果用户像我一样下滑了不就露馅了了么?本着求知心态,我将手机设置为3G网络,快速滑动抖音首页,出现加载后,我又下滑观察,抖音是有翻页效果的。摸清套路后我开始思考我该怎么做?

审查代码后发现scrollViewDidScroll中我只判断了用户上拉,没有处理下滑,那就再加上下滑判断。
整理后代码如下

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{[self.tableView jp_scrollViewDidScroll];int index= (int)self.tableView.contentOffset.y/kHeight;//scroll是与整屏相比的偏移量,肯定是正的float scroll = self.tableView.contentOffset.y- index*kHeight;//与上一个滑动点比较,区分上滑还是下滑float offset = self.tableView.contentOffset.y- oldOffset.y;//记录当前tableView.contentOffsetoldOffset = self.tableView.contentOffset;if (offset>0) {//上拉-44是mj_footer的高度,当拖拽超过44的时候会触发mjif (playIndex==_tableView.items.count-1&&scroll>44) {if (_tableView.updating==NO) {//判断是否正在刷新,正在刷新就不再进行如下设置,以免重复加载_tableView.updating = YES;//进到这里说明用户正在上拉加载,触发mj,此时要关闭翻页功能否则页面回弹mj_footer就看不到了,setContentOffset也无效self.tableView.pagingEnabled = NO;//给tableView设置一个固定的Offset,往上偏移点,将footer展示出来,要大于44才会触发footer[self.tableView setContentOffset:CGPointMake(0, index*kHeight+50) animated:NO];[self.tableView.mj_footer beginRefreshing];}}}else if (offset<0){if (_tableView.updating==YES) {//如果用户上拉加载时,又进行下滑操作,就要打开翻页功能(可能加载时间长用户不想等又往上翻之前的cell)-这种情况少见但不排除,不做此操作的话,将请求延时十秒就会看到区别,但一旦用户有这种操作就会有闪屏问题,即用户在第10个cell上拉加载了,然后又下滑倒第5个cell,当拿到返回数据之后页面会从5自动滚动到第11个cell,造成闪屏,但在3G网络下经测试抖音也是这样,故就这样吧self.tableView.pagingEnabled = YES;}}
}

到此就算搞定上拉加载了。

如果你使用时有什么问题,请留言。

感谢NewPan大神的JPVideoPlayer实现了抖音的自动播放

如果对您有帮助记得点赞收藏哦^ _ ^

 



作者:乔兰伊雪
链接:https://www.jianshu.com/p/313d56c2854b
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

这篇关于OC-抖音上拉加载(你以为单纯用MJRefresh就能实现?那你就错了)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/403701

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现