iOS下拉、上拉刷新控件的封装
程序员文章站
2023-11-30 08:21:22
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
写博客的初心是希望大家共同交流成长,博主水平有限难免有偏颇之处,欢迎批评指正。
下一篇: iOS设置圆角阴影 避免离屏渲染