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

这篇具有很好参考价值的文章主要介绍了IOS 类似抖音下拉刷新与自定义上拉加载。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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 *)touches
           withEvent:(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文章来源地址https://www.toymoban.com/news/detail-488539.html

到了这里,关于IOS 类似抖音下拉刷新与自定义上拉加载的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【HarmonyOS开发】ArkUI实现下拉刷新/上拉加载

     列表下拉刷新、上拉加载更多,不管在web时代还是鸿蒙应用都是一个非常常用的功能,基于ArkUI中TS扩展的声明式开发范式实现一个下拉刷新,上拉加载。 如果数据量过大,可以使用LazyForEach代替ForEach 高阶组件-上拉加载,下拉刷新 https://gitee.com/bingtengaoyu/harmonyos-advanced-com

    2024年01月20日
    浏览(41)
  • Android 实现 RecyclerView下拉刷新,SwipeRefreshLayout上拉加载

    上拉、下拉的效果图如下: 使用步骤 1、在清单文件中添加依赖 implementation ‘com.android.support:recyclerview-v7:27.1.1’ implementation “androidx.swiperefreshlayout:swiperefreshlayout:1.0.0” 2、main布局 item.xml footview.xml(底部提示) 2、MyAdapter 3、MainActivity实现

    2024年02月13日
    浏览(46)
  • H5如何做页面下拉刷新和上拉加载

    这里以vant为例 结构 处理方法

    2024年02月10日
    浏览(44)
  • flutter开发实战-下拉刷新与上拉加载更多实现

    flutter开发实战-下拉刷新与上拉加载更多实现 在开发中经常遇到列表需要下拉刷新与上拉加载更多,这里使用EasyRefresh,版本是3.3.2+1 EasyRefresh可以在Flutter应用程序上轻松实现下拉刷新和上拉加载。它几乎支持所有Flutter Scrollable小部件。它的功能与安卓的SmartRefreshLayout非常相似

    2024年02月07日
    浏览(44)
  • Flutter 库:强大的下拉刷新上拉加载框架——EasyRefresh

    EasyRefresh 是一个用于 Flutter 应用程序的简单易用的 下拉刷新 和 上拉加载 框架。它支持几乎所有的 Flutter 可滚动小部件。它的功能与Android 的 SmartRefreshLayout 非常相似,并吸收了许多第三方库的优点。EasyRefresh 集成了各种样式的页眉和页脚,但没有任何限制,您可以轻松自定

    2024年01月19日
    浏览(40)
  • 微信小程序 下拉分页 z-paging下拉刷新、上拉加载

    【z-paging下拉刷新、上拉加载】高性能,全平台兼容。支持虚拟列表,支持nvue、vue3 - DCloud 插件市场  z-paging,使用非常简单,按部就班就行了 1,首先将其导入自己的小程序项目中  导入后的效果 2,具体如何使用:https://z-paging.zxlee.cn    选项式api写法(vue2/vue3) 组合式api写法

    2024年02月11日
    浏览(59)
  • uniapp使用自带【刷新方法】与使用【scroll-view】实现下拉刷新上拉加载

    前言:uniapp自带下拉刷新,上拉加载功能基本可以满足刷新需求,但是顶部有状态栏的页面就得进行特殊处理,使用scroll-view解决,状态栏会连带被下拉问题   1、uniapp自带下拉刷新、上拉加载 在page.json中对应页面路由设置【enablePullDownRefresh】值为true(开启下拉刷新) 代码:

    2024年02月11日
    浏览(48)
  • 学习微信小程序的下拉刷新和上拉加载更多

    好的,下面我将为你详细介绍微信小程序中的下拉刷新和上拉加载更多功能,并提供代码案例。 下拉刷新功能 下拉刷新是指当用户在小程序页面下拉时,页面可以重新加载最新的数据。为了实现下拉刷新功能,我们需要使用小程序提供的 onPullDownRefresh 生命周期函数。 以一个

    2024年04月14日
    浏览(46)
  • 微信小程序 - scroll-view组件之上拉加载下拉刷新(解决上拉加载不触发)

    最近在做微信小程序项目中,有一个功能就是做一个商品列表分页限流然后实现上拉加载下拉刷新功能,遇到了一个使用scroll-viwe组件下拉刷新事件始终不触发问题,网上很多说给scroll-view设置一个高度啥的就可以解决,有些人设置了高度也不触发,所以在下就研究了一波这个

    2024年02月14日
    浏览(44)
  • 小白学习微信小程序的下拉刷新和上拉加载更多

    微信小程序是一种基于微信生态的轻量级应用程序,与传统的Web应用程序相比,微信小程序具有更低的开发成本、更高的运行速度和更好的用户体验。在微信小程序开发中,下拉刷新和上拉加载更多是非常常用的功能,并且在实现上也比较简单。在本篇文章中,我们将详细介

    2024年02月05日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包