iis服务器助手广告
返回顶部
首页 > 资讯 > 移动开发 >iOS下拉、上拉刷新控件的封装
  • 919
分享到

iOS下拉、上拉刷新控件的封装

iOS刷新控件 2022-05-18 00:05:05 919人浏览 安东尼
摘要

iOS 封装下拉、上拉刷新控件,首先看下效果图: 简单阐述一下:自定义头部、尾部刷新视图,继承UIView,通过KVO监听scrollView的滑动,通过偏移量设置刷新状态,通过修

iOS 封装下拉、上拉刷新控件,首先看下效果图:

简单阐述一下:自定义头部、尾部刷新视图,继承UIView,通过KVO监听scrollView的滑动,通过偏移量设置刷新状态,通过修改状态修改scrollView的滚动位置。建一个UIScrollView的分类,添加上拉、下拉刷新及回调的方法,可以让UITableView、UICollectionView直接调用。现在很多应用是在滑动到底部自动进行上拉加载超做,可以在scrollViewDidScroll这个代理方法中手动调用尾部刷新。

下面贴上主要相关代码:

控制器ViewController:


#import <UIKit/UIKit.h>
 
@interface ViewController : UIViewController
 
@end
 

 
#import "ViewController.h"
#import "HWRefresh.h"
 
@interface ViewController ()<UITableViewDataSource, UITableViewDelegate>
 
@property (nonatomic, strong) NSMutableArray *array;
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, assign) NSInteger page;
 
@end
 
@implementation ViewController
 
- (NSMutableArray *)array
{
 if (!_array) {
  _array = [NSMutableArray array];
 }
 
 return _array;
}
 
- (void)viewDidLoad {
 [super viewDidLoad];
 
 self.view.backgroundColor = [UIColor blackColor];
 self.page = 1;
 
 //模拟获取信息
 [self getInfo];
 
 //创建控件
 [self creatControl];
 
 //添加头部刷新
 [self addHeaderRefresh];
 
 //添加尾部刷新
 [self addFooterRefresh];
}
 
- (void)getInfo
{
 NSArray *array = @[@"iOS HERO博客", @"iOS HERO博客", @"iOS HERO博客", @"iOS HERO博客", @"Http://blog.csdn.net/hero_wqb"];
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  if (self.page == 1) {
   self.array = [NSMutableArray arrayWithArray:array];
  }else{
   [self.array addObjectsFromArray:array];
  }
  [_tableView reloadData];
  [_tableView headerEndRefreshing];
  [_tableView footerEndRefreshing];
  NSLog(@"已经刷新好了");
 });
}
 
- (void)creatControl
{
 //列表视图
 _tableView = [[UITableView alloc] initWithFrame:CGRectMake(20, 64, [[UIScreen mainScreen] bounds].size.width - 100, [[UIScreen mainScreen] bounds].size.height - 164) style:UITableViewStylePlain];
 _tableView.dataSource = self;
 _tableView.delegate = self;
 [self.view addSubview:_tableView];
}
 
- (void)addHeaderRefresh
{
 __weak typeof(self) weakSelf = self;
 [_tableView addHeaderRefreshWithCallback:^{
  __strong typeof(weakSelf) strongSelf = weakSelf;
  strongSelf.page = 1;
  [strongSelf getInfo];
 }];
}
 
- (void)addFooterRefresh
{
 __weak typeof(self) weakSelf = self;
 [_tableView addFooterRefreshWithCallback:^{
  __strong typeof(weakSelf) strongSelf = weakSelf;
  strongSelf.page ++;
  [strongSelf getInfo];
 }];
}
 
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
 return self.array.count;
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
 static NSString *identifier = @"refreshTest";
 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
 if (!cell) {
  cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
 }
 cell.textLabel.text = [_array[indexPath.row] stringByAppendingString:[NSString stringWithFORMat:@"_%ld", indexPath.row]];
 
 return cell;
}
 
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
 //滑动到底部自动刷新
 if (_tableView.contentSize.height > _tableView.frame.size.height && _tableView.contentOffset.y + _tableView.frame.size.height > _tableView.contentSize.height - 40 && _page < 50) {
  [_tableView footerBeginRefreshing];
 }
}
 
@end

刷新基类HWRefresHBaseView:


#import <UIKit/UIKit.h>
 
#define HWRefreshContentOffset @"contentOffset"
 
typedef enum {
 HWRefreshStateNormal = 0, //普通状态
 HWRefreshStatePulling,  //释放即可刷新的状态
 HWRefreshStateRefreshing, //正在刷新中的状态
} HWRefreshState;
 
@interface HWRefreshBaseView : UIView
 
@property (nonatomic, weak) UIScrollView *scrollView;
@property (nonatomic, copy) NSString *pullToRefreshText;
@property (nonatomic, copy) NSString *releaseToRefreshText;
@property (nonatomic, copy) NSString *refreshingText;
@property (nonatomic, copy) void (^refreshinGCallback)();
@property (nonatomic, assign) HWRefreshState state;
@property (nonatomic, assign) UIEdgeInsets scrollViewOriginalInset;
 
- (void)beginRefreshing;
- (void)endRefreshing;
 
@end
 

 
#import "HWRefreshBaseView.h"
 
#define KHWRefreshViewHeight 44.0f
#define KImageW 30.0f
#define KLabelW 100.0f
 
@interface HWRefreshBaseView ()
 
@property (nonatomic, weak) UILabel *rLabel;
@property (nonatomic, weak) UIImageView *rImageView;
 
@end
 
@implementation HWRefreshBaseView
 
- (instancetype)initWithFrame:(CGRect)frame
{
 frame.size.height = KHWRefreshViewHeight;
 if (self = [super initWithFrame:frame]) {
  CGFloat imageH = 30.f;
  CGFloat labelH = 20.f;
  CGFloat imageX = ([UIScreen mainScreen].bounds.size.width - KImageW - KLabelW) * 0.5;
  CGFloat imageY = (KHWRefreshViewHeight - imageH) * 0.5;
  CGFloat labelY = (KHWRefreshViewHeight - labelH) * 0.5;
  
  //图片
  UIImageView *rImageView = [[UIImageView alloc] initWithFrame:CGRectMake(imageX, imageY, KImageW, imageH)];
  rImageView.image = [UIImage imageNamed:@"refreshing.jpg"];
  [self addSubview:rImageView];
  self.rImageView = rImageView;
  
  //标签
  UILabel *rLabel = [[UILabel alloc] initWithFrame:CGRectMake(CGRectGetMaxX(rImageView.frame), labelY, KLabelW, labelH)];
  rLabel.text = self.pullToRefreshText;
  rLabel.font = [UIFont systemFontOfSize:14.0f];
  rLabel.textAlignment = NSTextAlignmentCenter;
  [self addSubview:rLabel];
  self.rLabel = rLabel;
 }
 
 return self;
}
 
- (void)willMoveToSuperview:(UIView *)newSuperview
{
 [super willMoveToSuperview:newSuperview];
 
 //旧的父控件
 [self.superview removeObserver:self forKeyPath:HWRefreshContentOffset context:nil];
 
 //新的父控件
 if (newSuperview) {
  [newSuperview addObserver:self forKeyPath:HWRefreshContentOffset options:NSKeyValueObservinGoptionNew context:nil];
  
  //记录UIScrollView
  _scrollView = (UIScrollView *)newSuperview;
  
  //记录UIScrollView最开始的contentInset
  _scrollViewOriginalInset = _scrollView.contentInset;
 }
 
 //居中显示图片、提示信息
 CGRect temFrame = _rImageView.frame;
 temFrame.origin.x = (newSuperview.frame.size.width - KImageW - KLabelW) * 0.5;
 _rImageView.frame = temFrame;
 
 CGRect tf = _rLabel.frame;
 tf.origin.x = CGRectGetMaxX(_rImageView.frame);
 _rLabel.frame = tf;
}
 
- (void)setPullToRefreshText:(NSString *)pullToRefreshText
{
 _pullToRefreshText = pullToRefreshText;
 
 self.rLabel.text = pullToRefreshText;
}
 
- (void)setState:(HWRefreshState)state
{
 if (_state == state) return;
 
 switch (state) {
  case HWRefreshStateNormal: {
   [self stopAnimating];
   self.rLabel.text = self.pullToRefreshText;
   break;
  }
   
  case HWRefreshStatePulling: {
   self.rLabel.text = self.releaseToRefreshText;
   break;
  }
   
  case HWRefreshStateRefreshing: {
   [self startAnimating];
   self.rLabel.text = self.refreshingText;
   if (self.refreshingCallback) self.refreshingCallback();
   break;
  }
   
  default:
   break;
 }
 
 _state = state;
}
 
//开始刷新
- (void)beginRefreshing
{
 self.state = HWRefreshStateRefreshing;
}
 
//结束刷新
- (void)endRefreshing
{
 self.state = HWRefreshStateNormal;
}
 
//开始动画
- (void)startAnimating
{
 NSMutableArray *array = [NSMutableArray array];
 for (int i = 0; i < 2; i++) {
  NSString *imageName = [NSString stringWithFormat:@"refreshing%02d.jpg", i + 1];
  UIImage *image = [UIImage imageNamed:imageName];
  [array addObject:image];
 }
 
 [_rImageView setAnimationImages:array];
 [_rImageView setAnimationDuration:0.3f];
 [_rImageView startAnimating];
}
 
//结束动画
- (void)stopAnimating
{
 if (_rImageView.isAnimating) {
  [_rImageView stopAnimating];
  [_rImageView performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:0];
 }
}
 
@end

头部刷新HWRefreshHeader:


#import "HWRefreshBaseView.h"
 
@interface HWRefreshHeader : HWRefreshBaseView
 
+ (instancetype)header;
 
@end
 

 
#import "HWRefreshHeader.h"
 
@implementation HWRefreshHeader
 
+ (instancetype)header
{
 return [[HWRefreshHeader alloc] init];
}
 
- (instancetype)initWithFrame:(CGRect)frame
{
 if (self = [super initWithFrame:frame]) {
  self.pullToRefreshText = @"下拉即可刷新";
  self.releaseToRefreshText = @"释放即可刷新";
  self.refreshingText = @"刷新中...";
 }
 
 return self;
}
 
- (void)willMoveToSuperview:(UIView *)newSuperview
{
 [super willMoveToSuperview:newSuperview];
 
 //设置自己的位置和尺寸
 CGRect frame = self.frame;
 frame.origin.y = - self.frame.size.height;
 self.frame = frame;
}
 
 
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
 //不能跟用户交互或正在刷新就直接返回
 if (!self.userInteractionEnabled || self.alpha <= 0.01 || self.hidden || self.state == HWRefreshStateRefreshing) return;
 
 //根据偏移量设置相应状态
 if ([keyPath isEqualToString:HWRefreshContentOffset]) {
  [self setStateWithContentOffset];
 }
}
 
- (void)setStateWithContentOffset
{
 //当前的contentOffset
 CGFloat currentOffsetY = self.scrollView.contentOffset.y;
 
 //头部控件刚好出现的offsetY
 CGFloat happenOffsetY = - self.scrollViewOriginalInset.top;
 
 //如果是向上滚动到看不见头部控件,直接返回
 if (currentOffsetY >= happenOffsetY) return;
 
 //滑动时
 if (self.scrollView.isDragging) {
  //普通状态和即将刷新状态的临界点
  CGFloat normalTopullingOffsetY = happenOffsetY - self.frame.size.height;
  
  //转为即将刷新状态
  if (self.state == HWRefreshStateNormal && currentOffsetY < normalTopullingOffsetY) {
   self.state = HWRefreshStatePulling;
   
  //转为普通状态
  }else if (self.state == HWRefreshStatePulling && currentOffsetY >= normalTopullingOffsetY) {
   self.state = HWRefreshStateNormal;
  }
  
 //松手时,如果是松开就可以进行刷新的状态,则进行刷新
 }else if (self.state == HWRefreshStatePulling) {
  self.state = HWRefreshStateRefreshing;
 }
}
 
- (void)setState:(HWRefreshState)state
{
 //若状态未改变,直接返回
 if (self.state == state) return;
 
 //保存旧状态
 HWRefreshState oldState = self.state;
 
 //调用父类方法
 [super setState:state];
 
 switch (state) {
  case HWRefreshStateNormal: {
   //如果由刷新状态返回到普通状态
   if (oldState == HWRefreshStateRefreshing) {
    [UIView animateWithDuration:0.25f animations:^{
     UIEdgeInsets inset = self.scrollView.contentInset;
     inset.top -= self.frame.size.height;
     self.scrollView.contentInset = inset;
    }];
   }
   break;
  }
   
  case HWRefreshStatePulling: {
   break;
  }
   
  case HWRefreshStateRefreshing: {
   //执行动画
   [UIView animateWithDuration:0.25f animations:^{
    CGFloat top = self.scrollViewOriginalInset.top + self.frame.size.height;
    
    //增加滚动区域
    UIEdgeInsets inset = self.scrollView.contentInset;
    inset.top = top;
    self.scrollView.contentInset = inset;
    
    //设置滚动位置
    CGPoint offset = self.scrollView.contentOffset;
    offset.y = - top;
    self.scrollView.contentOffset = offset;
   }];
   break;
  }
   
  default:
   break;
 }
 
 self.state = state;
}
 
@end

分类UIScrollView+HWRefresh:


#import <UIKit/UIKit.h>
 
@interface UIScrollView (HWRefresh)
 
//添加下拉刷新回调
- (void)addHeaderRefreshWithCallback:(void (^)())callback;
 
//让下拉刷新控件停止刷新
- (void)headerEndRefreshing;
 
//添加上拉刷新回调
- (void)addFooterRefreshWithCallback:(void (^)())callback;
 
//让上拉刷新控件开始刷新
- (void)footerBeginRefreshing;
 
//让上拉刷新控件停止刷新
- (void)footerEndRefreshing;
 
@end
 

 
#import "UIScrollView+HWRefresh.h"
#import "HWRefreshHeader.h"
#import "HWRefreshFooter.h"
#import <objc/runtime.h>
 
@interface UIScrollView ()
 
@property (nonatomic, weak) HWRefreshHeader *header;
@property (weak, nonatomic) HWRefreshFooter *footer;
 
@end
 
@implementation UIScrollView (HWRefresh)
 
static char HWRefreshHeaderKey;
static char HWRefreshFooterKey;
 
- (void)setHeader:(HWRefreshHeader *)header
{
 [self willChangeValueForKey:@"HWRefreshHeaderKey"];
 objc_setAssociatedObject(self, &HWRefreshHeaderKey, header, OBJC_ASSOCIATION_ASSIGN);
 [self didChangeValueForKey:@"HWRefreshHeaderKey"];
}
 
- (HWRefreshHeader *)header
{
 return objc_getAssociatedObject(self, &HWRefreshHeaderKey);
}
 
- (void)setFooter:(HWRefreshFooter *)footer
{
 [self willChangeValueForKey:@"HWRefreshFooterKey"];
 objc_setAssociatedObject(self, &HWRefreshFooterKey, footer, OBJC_ASSOCIATION_ASSIGN);
 [self didChangeValueForKey:@"HWRefreshFooterKey"];
}
 
- (HWRefreshFooter *)footer
{
 return objc_getAssociatedObject(self, &HWRefreshFooterKey);
}
 
- (void)addHeaderRefreshWithCallback:(void (^)())callback
{
 if (!self.header) {
  HWRefreshHeader *header = [HWRefreshHeader header];
  [self addSubview:header];
  self.header = header;
 }
 
 self.header.refreshingCallback = callback;
}
 
- (void)headerEndRefreshing
{
 [self.header endRefreshing];
}
 
- (void)addFooterRefreshWithCallback:(void (^)())callback
{
 if (!self.footer) {
  HWRefreshFooter *footer = [HWRefreshFooter footer];
  [self addSubview:footer];
  self.footer = footer;
 }
 
 self.footer.refreshingCallback = callback;
}
 
- (void)footerBeginRefreshing
{
 [self.footer beginRefreshing];
}
 
- (void)footerEndRefreshing
{
 [self.footer endRefreshing];
}
 
@end

Demo下载链接

写博客的初心是希望大家共同交流成长,博主水平有限难免有偏颇之处,欢迎批评指正。

--结束END--

本文标题: iOS下拉、上拉刷新控件的封装

本文链接: https://www.lsjlt.com/news/30335.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 基于vue封装下拉刷新上拉加载组件
    基于vue和原生javascript封装的下拉刷新上拉加载组件,供大家参考,具体内容如下 upTilte插槽是下拉刷新的自定义内容放的地方 downTilte插槽是...
    99+
    2024-04-02
  • html5如何封装下拉刷新
    这篇文章主要介绍了html5如何封装下拉刷新,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。前端在工作当中难免会于原生的安卓和ios合作,去做一些H5嵌套的页面。但是实际开发中...
    99+
    2023-06-09
  • Android中的RecyclerView下拉/上拉刷新数据
            在Android中的列表视图(我们这里以RecyclerView为例)中有很多数据的时候,往往要采取限制数据条目显示,然后通过刷新再添加新的数据显示,这样看的就会比较美观,那么这种列表视图是怎么实现刷新的呢,我们一起来看看吧...
    99+
    2023-09-07
    android
  • dropload.js插件下拉刷新和上拉加载怎么用
    小编给大家分享一下dropload.js插件下拉刷新和上拉加载怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!具体内容如下第...
    99+
    2024-04-02
  • Flutter实现下拉刷新和上拉加载更多
    本文实例为大家分享了Flutter实现下拉刷新和上拉加载更多的具体代码,供大家参考,具体内容如下 效果 下拉刷新 如果实现下拉刷新,必须借助RefreshIndicator,在li...
    99+
    2024-04-02
  • 小程序怎么实现上拉刷新下拉加载
    这篇文章主要介绍“小程序怎么实现上拉刷新下拉加载”,在日常操作中,相信很多人在小程序怎么实现上拉刷新下拉加载问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”小程序怎么实现上拉刷新下拉加载”的疑惑有所帮助!接下来...
    99+
    2023-06-26
  • MUI如何实现上拉刷新/下拉加载功能
    小编给大家分享一下MUI如何实现上拉刷新/下拉加载功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!新闻信息列表必备的功能,支持...
    99+
    2024-04-02
  • 浅谈Vant-list 上拉加载及下拉刷新的问题
    目录Vant-list 上拉加载及下拉刷新vant下拉刷新与上拉加载一起使用问题下拉刷新触发两次 list与pull解决方法是Vant-list 上拉加载及下拉刷新 第一步引入 im...
    99+
    2024-04-02
  • 原生js实现下拉刷新和上拉加载更多
    本文实例为大家分享了js实现下拉刷新和上拉加载更多的具体代码,供大家参考,具体内容如下 1.下拉刷新 由于原生js太久不用了,这里列一下此处涉及到的前置知识点: 移动端触屏事件: t...
    99+
    2024-04-02
  • uniapp怎么实现下拉刷新和上拉加载功能
    随着移动端开发的不断升级,开发者们对于移动应用的需求也越来越高。而在很多移动应用中,下拉刷新和上拉加载更多是必不可少的功能之一,为了提高用户体验,许多移动应用都会加入这两个功能。在这里,我们将介绍如何在uniapp中实现下拉刷新和上拉加载更...
    99+
    2023-05-14
  • Spring怎么实现上拉刷新和下拉加载效果
    这篇文章主要介绍Spring怎么实现上拉刷新和下拉加载效果,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!导依赖:compile 'com.android.support:recyclerview-v...
    99+
    2023-05-30
    spring
  • MUI如何实现上拉加载和下拉刷新效果
    这篇文章主要介绍了MUI如何实现上拉加载和下拉刷新效果,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。编写存储过程分页(此处使用T-SQL)C...
    99+
    2024-04-02
  • Vue uni-app框架实现上拉加载下拉刷新功能
    目录实现上拉加载更多优化实现下拉刷新实现上拉加载更多 打开项目根目录中的pages.json配置文件,为subPackages分包中的商品goods_list页面配置上拉触底的距离:...
    99+
    2024-04-02
  • React Native如何自定义下拉刷新上拉加载的列表
    小编给大家分享一下React Native如何自定义下拉刷新上拉加载的列表,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!在移动端...
    99+
    2024-04-02
  • Vue vant-ui框架实现上拉加载下拉刷新功能
    目录知识点速记基本用法下拉刷新代码实现1.页面布局2.样式3.方法下拉刷新效果: 知识点速记 基本用法 List通过loading和finished两个变量控制加载状态,当组件滚动...
    99+
    2024-04-02
  • Vant-list上拉加载及下拉刷新问题怎么解决
    本篇内容介绍了“Vant-list上拉加载及下拉刷新问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Vant-list 上拉加载及...
    99+
    2023-06-30
  • Android自定义ListView实现下拉刷新上拉加载更多
    目录1、创建刷新控件1.1、创建头部View1.2、下拉事件1.3、接口回调2、上拉加载更多2.1、底部样式2.2、布局添加Listview现在用的很少了,基本都是使用Recycle...
    99+
    2024-04-02
  • Flutter listview如何实现下拉刷新上拉加载更多功能
    目录下拉刷新 RefreshIndicator 上拉加载更多 总结:下拉刷新 在Flutter中系统已经为我们提供了google material design的刷新功能 , 样式...
    99+
    2024-04-02
  • 怎么用vue代码实现下拉刷新,上拉更多功能
    这篇文章主要讲解了“怎么用vue代码实现下拉刷新,上拉更多功能”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用vue代码实现下拉刷新,上拉更多功能”吧!具体代码如下:<templa...
    99+
    2023-07-04
  • vue基于vant实现上拉加载下拉刷新的示例代码
    前言 普遍存在于各种app中的上拉加载下拉刷新功能大家都不陌生吧,一般来说,在数据量比较大的情况下,为了更快的渲染和给用户更好的观感体验,我们会将数据做分页处理,让其批量加载,这样一...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作