欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-08-17 20:59:23
ios 封装下拉、上拉刷新控件,首先看下效果图: 简单阐述一下:自定义头部、尾部刷新视图,继承uiview,通过kvo监听scrollview的滑动,通过偏移量设置刷...

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

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下载链接

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