IOS 类似抖音下拉刷新与自定义上拉加载

最近UICollectionView中使用了pageEnabled,MJRresh直接使用时出现偏移。这里就暂时考虑简单的做法。

首先考虑在UICollection与拖动手势之间的问题。

解决UICollectionView上添加手势不能触发。

这里使用了子类继承UICollectionView

#import "INMyCollectionView.h"@implementation INMyCollectionView-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {[[self nextResponder] touchesBegan:touches withEvent:event];[super touchesBegan:touches withEvent:event];
}-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {[[self nextResponder] touchesMoved:touches withEvent:event];[super touchesMoved:touches withEvent:event];
}- (void)touchesEnded:(NSSet *)toucheswithEvent:(UIEvent *)event {[[self nextResponder] touchesEnded:touches withEvent:event];[super touchesEnded:touches withEvent:event];
}@end

在View上添加拖动手势

self.startPoint = CGPointZero;
self.isInLoading = NO;

UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureHandle:)];
panGesture.minimumNumberOfTouches = 1;
panGesture.maximumNumberOfTouches = 1;
panGesture.delegate = self;
[self addGestureRecognizer:panGesture];

处理手势判断

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {return YES;
}

// 给加的手势设置代理, 并实现此协议方法

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gestureRecognizer;CGPoint pos = [pan velocityInView:pan.view];if (pos.y > 0) {return YES;}}return NO;
}

在处理拖动手势

 (void)panGestureHandle:(UIPanGestureRecognizer *)pan{if (self.collectionView.contentOffset.y > 0) {return;}DebugLog(@"panGestureHandle");if (self.isInLoading) {return;}if (pan.state == UIGestureRecognizerStateBegan) {DebugLog(@"UIGestureRecognizerStateBegan");self.startPoint = [pan translationInView:self];self.collectionView.scrollEnabled = NO;} if (pan.state == UIGestureRecognizerStateChanged) {DebugLog(@"UIGestureRecognizerStateChanged");CGPoint point = [pan translationInView:self];CGFloat distance = point.y - self.startPoint.y;if (distance > 0) {self.refreshStateLabel.text = @"下拉刷新";CGFloat scale = distance/KMaxScrollRefreshHeight;if (scale > 1.0) {scale = 1.0;}self.refreshStateLabel.alpha = scale;self.navbarView.alpha = (1-scale);self.refreshStateLabel.frame = CGRectMake(0.0, scale*(kStatusBarHeight + [BaseView baseSafeAreaEdgeInsets].top) + 5.0, CGRectGetWidth(self.bounds), 44.0);if (distance > KMaxScrollRefreshHeight) {DebugLog(@"可以下拉刷新");}} else {DebugLog(@"上拉操作");self.refreshStateLabel.alpha = 0.0;}} else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled || pan.state == UIGestureRecognizerStateFailed) {DebugLog(@"UIGestureRecognizerStateEnded");self.collectionView.scrollEnabled = YES;CGPoint point = [pan translationInView:self];CGFloat distance = point.y - self.startPoint.y;self.refreshStateLabel.text = @"";if (distance > 0) {if (distance > KMaxScrollRefreshHeight) {DebugLog(@"加载中...");self.refreshStateLabel.text = @"加载中...";self.refreshStateLabel.alpha = 1.0;self.isInLoading = YES;if (self.actionDelegate && [self.actionDelegate respondsToSelector:@selector(refreshLoadingData)]) {[self.actionDelegate refreshLoadingData];}}}}
}当然,类似抖音的下拉,在ScrollDidScoll处理了下
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {if (scrollView.contentOffset.y < 0) {[scrollView setContentOffset:CGPointMake(0.0, 0.0)];} else {// 向上拖动,向上拖动的指定位置后加载更多数据DebugLog(@"向上拖动,向上拖动的指定位置后加载更多数据");}
}

第二部分

自己处理上拉加载更多,这里代码如下
INUpwardRefreshView.h

@protocol INUpwardRefreshViewDelegate;@interface INUpwardRefreshView : BaseView@property (nonatomic, weak) id<INUpwardRefreshViewDelegate>delegate;
@property (nonatomic, strong) UIScrollView *scrollView;- (instancetype)initWithTarget:(UIScrollView *)scrollView;- (void)endRefreshing;@end@protocol INUpwardRefreshViewDelegate <NSObject>- (void)pullUpwardRefreshDidFinish;@end

INUpwardRefreshView.m

//
//  INUpwardRefreshView.m
//  Views
//
//  Created by ABC on 2019/7/26.
//#import "INUpwardRefreshView.h"
#import "Color.h"
#import "NSString+size.h"NSString *const RefreshUpKeyPathContentOffset = @"contentOffset";
NSString *const RefreshUpKeyPathContentSize = @"contentSize";CGFloat const FooterUpHeight = 80;@interface INUpwardRefreshView ()@property (nonatomic, strong) UIActivityIndicatorView *activityView;
@property (nonatomic, strong) UIImageView *arrowView;
@property (nonatomic, strong) UILabel *stateLable;
@property (nonatomic, assign) BOOL isRefresh;
@property (nonatomic, assign) CGFloat lastOffSet;@end@implementation INUpwardRefreshView- (instancetype)initWithTarget:(UIScrollView *)scrollView {self = [super init];if (self) {self.backgroundColor = [UIColor clearColor];_scrollView = scrollView;self.frame = CGRectMake(0, 0, 0, FooterUpHeight);[_scrollView addSubview:self];[self addObserver];}return self;}- (void)layoutSubviews{[super layoutSubviews];CGSize statelabelSize = [self.stateLable.text sizeWithFont:self.stateLable.font forMaxSize:CGSizeMake(MAXFLOAT, 40)];self.stateLable.frame = CGRectMake((CGRectGetWidth(self.bounds) - statelabelSize.width)/2 + 10.0, 0.0, statelabelSize.width, 40);self.arrowView.frame = CGRectMake(CGRectGetMinX(self.stateLable.frame) - CGRectGetWidth(self.arrowView.frame) - 10.0, 0.0, CGRectGetWidth(self.arrowView.frame), 40);self.activityView.frame = CGRectMake(CGRectGetMinX(self.stateLable.frame) - CGRectGetWidth(self.arrowView.frame) - 10.0, (40.0 - 20.0)/2, 20, 20);
}- (void)setCurrentFrame {[self setFrame:CGRectMake(0, MAX(self.scrollView.contentSize.height, CGRectGetHeight(self.scrollView.frame)), self.scrollView.size.width, FooterUpHeight)];[self setNeedsLayout];
}- (void)setFrameHeight:(CGFloat)height {[self setFrame:CGRectMake(0, MAX(self.scrollView.contentSize.height, CGRectGetHeight(self.scrollView.frame)), self.scrollView.size.width, height)];DebugLog(@"frameHeight:%f",height);
}#pragma mark setter
- (UIActivityIndicatorView *)activityView{if (!_activityView) {_activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];_activityView.hidesWhenStopped = YES;[self addSubview:_activityView];}return _activityView;
}- (UIImageView *)arrowView{if (!_arrowView) {_arrowView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 15, 40)];_arrowView.image = [UIImage imageNamed:@"refresh_up_arrow"];//[self addSubview:_arrowView];}return _arrowView;
}- (UILabel *)stateLable{if (!_stateLable) {_stateLable = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 90, 40)];_stateLable.backgroundColor = [UIColor clearColor];_stateLable.font = [UIFont systemFontOfSize:12];_stateLable.textAlignment = NSTextAlignmentLeft;_stateLable.textColor = [UIColor whiteColor];[self addSubview:_stateLable];}return _stateLable;
}#pragma mark private
- (void)scrollViewContentOffsetDidChange:(NSDictionary *)change{_stateLable.hidden = NO;CGFloat yOffSet = _scrollView.contentOffset.y;DebugLog(@"yOffSet:%f",yOffSet);CGFloat contentSizeHeight = _scrollView.contentSize.height;CGFloat frameHeight = CGRectGetHeight(_scrollView.frame);if (contentSizeHeight > 0 && contentSizeHeight < frameHeight) {CGFloat insetTop = frameHeight - contentSizeHeight;if (yOffSet+insetTop > 0.0) {//正在拖拽中if (self.scrollView.isDragging) {[UIView animateWithDuration:0.3 animations:^{self.arrowView.hidden = NO;if (yOffSet+insetTop > 100.0) {self.arrowView.transform =  CGAffineTransformMakeRotation(M_PI);self.stateLable.text = @"松开夹子";}else{self.arrowView.transform =  CGAffineTransformMakeRotation(M_PI*2);self.stateLable.text = @"继续滑动";}}];[self setFrameHeight:(yOffSet+insetTop)];} else {if (yOffSet+insetTop > 100.0) {[self beginRefreshing];}}}} else {//正在拖拽中CGFloat aYBottom = yOffSet - (contentSizeHeight - frameHeight);if (self.scrollView.isDragging) {[UIView animateWithDuration:0.3 animations:^{self.arrowView.hidden = NO;if (aYBottom > 100.0) {self.arrowView.transform =  CGAffineTransformMakeRotation(M_PI);self.stateLable.text = @"松开加载";}else{self.arrowView.transform =  CGAffineTransformMakeRotation(M_PI*2);self.stateLable.text = @"继续滑动";}}];[self setFrameHeight:yOffSet];} else {if (contentSizeHeight > 0 && aYBottom > 100.0) {[self beginRefreshing];}}}
}#pragma mark publick
- (void)beginRefreshing{[self setFrameHeight:FooterUpHeight];if (!_isRefresh) {_isRefresh = YES;CGFloat insetTop = _scrollView.contentInset.top;//设置偏移量,衔接加载的更多数据[UIView animateWithDuration:0.35 animations:^{//[self.scrollView setContentInset:UIEdgeInsetsMake(insetTop, 0, FooterUpHeight, 0)];[self.activityView startAnimating];self.arrowView.hidden = YES;self.stateLable.text = @"加载中";} completion:^(BOOL finished) {[self startBeginRefresh];}];}
}- (void)startBeginRefresh {// Refresh action!if ([self.delegate respondsToSelector:@selector(pullUpwardRefreshDidFinish)]) {[self.delegate performSelector:@selector(pullUpwardRefreshDidFinish) withObject:nil];}
}- (void)endRefreshing {_isRefresh = NO;[UIView animateWithDuration:0.3 animations:^{[self.activityView stopAnimating];self.arrowView.hidden = YES;self.arrowView.transform =  CGAffineTransformMakeRotation(M_PI*2);}];
}#pragma mark KVO
- (void)addObserver{NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;[_scrollView addObserver:self forKeyPath:RefreshUpKeyPathContentOffset options:options context:nil];[_scrollView addObserver:self forKeyPath:RefreshUpKeyPathContentSize options:options context:nil];
}- (void)removeObserver{[_scrollView removeObserver:self forKeyPath:RefreshUpKeyPathContentOffset];[_scrollView removeObserver:self forKeyPath:RefreshUpKeyPathContentSize];
}- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{if ([keyPath isEqualToString:RefreshUpKeyPathContentOffset]) {[self scrollViewContentOffsetDidChange:change];} else if ([keyPath isEqualToString:RefreshUpKeyPathContentSize]) {[self setCurrentFrame];} else {[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];}
}- (void)dealloc{[self removeObserver];
}@end

使用上拉加载的shih
在collection创建的时候,设置下即可

_upwardRefreshView = [[INUpwardRefreshView alloc] initWithTarget:_collectionView];_upwardRefreshView.delegate = self;

源码地址:

https://github.com/goodbruce/DouyinRefresh

IOS 类似抖音下拉刷新与自定义上拉加载相关推荐

  1. JRoll 2 使用文档(史上最强大的下拉刷新,滚动,无限加载插件)

    概述 说明 JRoll,一款能滚起上万条数据,具有滑动加速.回弹.缩放.滚动条.滑动事件等功能,兼容CommonJS/AMD/CMD模块规范,开源,免费的轻量级html5滚动插件. JRoll第二版是 ...

  2. 仿新浪微博实现ListBox下拉刷新和到底部自动加载

    一.下拉刷新 下拉刷新实现思路: 1.定义一个PullDownToRefreshPanel容器控件.为它添加3种状态模板,分别是PullingDownTemplate,ReadyToReleaseTe ...

  3. vue移动端下拉刷新组件、上拉加载组件

    1.下拉刷新DropDownRefresh.vue <template> <div @touchstart="touchStart($event)" @touch ...

  4. 微信小程序上滑加载下拉刷新(onscrollLower)分批加载数据 下篇

    事件(wxml)>bindscrolltolower="onscrollLower" slice() 方法可从已有的数组中返回选定的元素. 请注意,该方法并不会修改数组,而是 ...

  5. Cocos Creator 3.2 本地调试正常 放入域名下运行或者在4399上跑 加载cconb文件报错

    https://blog.csdn.net/qq_39162566/article/details/124189655

  6. 抖音下拉框电脑版,同步移动端能上传视频!

    抖音大家现在都习惯手机上刷呀刷,但是好多优质的内容在手机屏幕上看起来太不爽了,这补,抖音也有推出了抖音下拉框网页版,应该是今年上半年出来的吧,数据和移动端同步的,想看什么直接搜索就可以了! 看下面的界 ...

  7. 浅谈抖音下拉词框优化推广的优势

    相信现在基本上,或者说绝大部分人的手机上都装了抖音短视频app.火山.快手app等这些短视频app.甚至包括现在遇到问题,大家都会打开抖音,然后去搜索自己遇到的问题,最后来找到自己想要的答案视频. 为 ...

  8. 抖音下拉框中的下拉词是怎么出来的?

    疑问:抖音下拉框中的下拉词是怎么出来的? 抖音下拉词展示设想 抖音下拉设想验证 抖音下拉词出现思路总结 抖音下拉词展示设想 昔年认为,抖音下拉词框中的下拉词出现与否,是根据关注热度去生成的,如果某一个 ...

  9. 餐饮加盟推广遇到抖音下拉词框会碰撞出什么样的火花

    疑问:餐饮加盟推广遇到抖音下拉词框会碰撞出什么样的火花? 1.前言 大伙都知道现在抖音.快手等上面,探店的,大胃王挑战.美食制作,美食加盟的视频特别多,那特别多说明了一点什么呢? 说明的现在大家开始重 ...

最新文章

  1. 今天的面试官是个锤子,Spring为什么建议使用构造器来注入?
  2. 为什么在系统中使用JSON
  3. 【SpringCloud】简介及其核心组件详解
  4. 十大被低估的python库_小白必读!十大被低估的Python自带库!
  5. 宽客的人amp;amp;事件映射
  6. JS 实现3D立体效果的首页轮播图(瞬间让你的网站高大上,逼格满满)
  7. Tensorflow练习题
  8. loj 6083.「美团 CodeM 资格赛」数码
  9. POJ3261 Milk Patterns 【后缀数组】
  10. 深入浅出设计模式之命令模式、适配器模式、外观模式
  11. Keys配置及使用说明
  12. [USACO09NOV]Job Hunt
  13. 《我爱我的祖国》受捧 再现专线另类舌尖上中国
  14. Qgis教程12:动态展示
  15. 哈希(hash)理解
  16. pip或pip3安装报错Could not fetch URL https://pypi.org/simple/pip/: There was a problem confirming。。。
  17. Linux操作系统PS命令详细 解析
  18. 阿里云服务器地域和可用区常见问题及官方资料解答
  19. OSChina 周三乱弹 ——grath最近睡了一主播
  20. 插入top.php,CmsTop手动区块(就是phpcms的碎片)的内容支持三种添加方式

热门文章

  1. ThingMap一键城市2.0重新出发:快速生成三维城市
  2. 计算机组成原理中指令的四个工作周期
  3. crmeb 电商系统 小程序商城系统接口
  4. Zotero使用指南-下载、文献导入与阅读、文献引用与插件使用
  5. Latex如何输入大型的中括号
  6. nsis mysql_三、NSIS实例
  7. Android中module怎么用?
  8. 拍照、相册及裁剪的终极实现(一)——拍照及裁剪功能实现
  9. 一个测试工程师的七年感悟——致在一路独行的你(别放弃)
  10. elasticsearch 学习笔记(查询语句和修改语句)