iOS自定义可展示、交互的scrollView滚动条
上一篇简述了封装,本篇在此基础上添加了一个自定义的scrollview滚动条,可展示、交互,首先看一下效果图:
简单阐述一下实现逻辑:自定义滚动条视图继承uiview,添加滚动条滑动事件、其他区域点击事件,通过代理方法与列表关联。在列表刷新完成及scrollview代理方法中更新滚动条。
简单说一下计算逻辑,如上图(原谅博主的图)所示,其中b、c、d是已知的。首先计算滚动条的高度a,理想情况下它与整个滚动区域b的比值应该等于scrollview的展示区域b与scrollview的内容高度d的比值,就是 a/b = b/d,即 a = b*b/d,也是就代码中的“_scrollbar.barheight = pow(tableview.bounds.size.height,2) / tableview.contentsize.height;”这句话。
既然是理想情况,就有特殊情况,首先如果内容高度d小于展示区域b,就是说不需要滑动时,这里可以有两种处理,第一种是隐藏滚动条,第二种是将滚动条高度设为与滚动区域一致,方便观察,这里使用后一种。还有一种特殊情况就是,如果内容区域d无限增大,则滚动条高度a无限减小,所以需要给定一个最小高度限制。
好了,上面计算出滚动条高度a,然后计算滚动条y向位置x,很容易看出来 x/b = c/d,正常情况下这是没有问题的,但是当滚动条高度非常小,小于我们设定的最小高度时就会有误差,那么换另一种写法 x/(b-a) = c/(d-b),即 x = (b-a)*c/(d-b),代码中“_scrollbar.yposition = (_scrollbar.bounds.size.height - _scrollbar.barheight) *_tableview.contentoffset.y / (_tableview.contentsize.height -_scrollbar.bounds.size.height);”这句话。那么在scrollview代理方法中更新这两项就实现了滚动条高度根据scrollview内容增减,并根据scrollview滑动而移动。
最后在我们自定义滚动条的代理方法中设置scrollview的contentoffset,即可实现scrollview随着滚动条的点击滑动而移动。计算方法与上面一致 x/(b-a) = c/(d-b),区别是这次动条y向位置x是已知的,scrollview的y向偏移量c是未知的,即 c = (d-b)*x/(b-a),代码中“[_tableviewsetcontentoffset:cgpointmake(0, (_tableview.contentsize.height -_scrollbar.bounds.size.height) * scrollbar.yposition / (_scrollbar.bounds.size.height - _scrollbar.barheight))];”这句话。
下面贴上相关代码:
控制器viewcontroller:
#import <uikit/uikit.h> @interface viewcontroller : uiviewcontroller @end /*** ---------------分割线--------------- ***/ #import "viewcontroller.h" #import "hwrefresh.h" #import "hwscrollbar.h" @interface viewcontroller ()<uitableviewdatasource, uitableviewdelegate, hwscrollbardelegate> @property (nonatomic, strong) nsmutablearray *array; @property (nonatomic, strong) uitableview *tableview; @property (nonatomic, weak) hwscrollbar *scrollbar; @property (nonatomic, weak) hwscrollbar *tablebar; @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", @"ios hero博客", @"ios hero博客", @"ios hero博客", @"ios hero博客", @"http://blog.csdn.net/hero_wqb"]; dispatch_after(dispatch_time(dispatch_time_now, (int64_t)(1.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]; //滚动展示条 hwscrollbar *tablebar = [[hwscrollbar alloc] initwithframe:cgrectmake(cgrectgetmaxx(_tableview.frame), cgrectgetminy(_tableview.frame), 5, _tableview.bounds.size.height)]; tablebar.forecolor = [uicolor greencolor]; tablebar.backcolor = [uicolor graycolor]; tablebar.userinteractionenabled = no; [self.view addsubview:tablebar]; _tablebar = tablebar; //滚动条 hwscrollbar *scrollbar = [[hwscrollbar alloc] initwithframe:cgrectmake(cgrectgetmaxx(_tableview.frame) + 20, cgrectgetminy(_tableview.frame), 40, _tableview.bounds.size.height)]; scrollbar.delegate = self; scrollbar.minbarheight = 80; [self.view addsubview:scrollbar]; _scrollbar = scrollbar; } - (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)tableview:(uitableview *)tableview willdisplaycell:(uitableviewcell *)cell forrowatindexpath:(nsindexpath *)indexpath { dispatch_after(dispatch_time(dispatch_time_now, (int64_t)(0.1f * nsec_per_sec)), dispatch_get_main_queue(), ^{ //更新滚动条高度 if (tableview.contentsize.height <= tableview.bounds.size.height) { _scrollbar.barheight = tableview.bounds.size.height; _tablebar.barheight = tableview.bounds.size.height; }else { _scrollbar.barheight = pow(tableview.bounds.size.height, 2) / tableview.contentsize.height; _tablebar.barheight = pow(tableview.bounds.size.height, 2) / tableview.contentsize.height; } //更新滚动条y向位置 _scrollbar.yposition = (_scrollbar.bounds.size.height - _scrollbar.barheight) * _tableview.contentoffset.y / (_tableview.contentsize.height - _scrollbar.bounds.size.height); _tablebar.yposition = (_tablebar.bounds.size.height - _tablebar.barheight) * _tableview.contentoffset.y / (_tableview.contentsize.height - _tablebar.bounds.size.height); }); } - (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]; } //更新滚动条位置 _scrollbar.yposition = (_scrollbar.bounds.size.height - _scrollbar.barheight) * scrollview.contentoffset.y / (scrollview.contentsize.height - _scrollbar.bounds.size.height); _tablebar.yposition = (_tablebar.bounds.size.height - _tablebar.barheight) * scrollview.contentoffset.y / (scrollview.contentsize.height - _tablebar.bounds.size.height); } #pragma mark - sxscrollbardelegate - (void)scrollbardidscroll:(hwscrollbar *)scrollbar { [_tableview setcontentoffset:cgpointmake(0, (_tableview.contentsize.height - _scrollbar.bounds.size.height) * scrollbar.yposition / (_scrollbar.bounds.size.height - _scrollbar.barheight))]; } - (void)scrollbartouchaction:(hwscrollbar *)scrollbar { [uiview animatewithduration:scrollbar.barmoveduration animations:^{ [_tableview setcontentoffset:cgpointmake(0, (_tableview.contentsize.height - _scrollbar.bounds.size.height) * scrollbar.yposition / (_scrollbar.bounds.size.height - _scrollbar.barheight))]; }]; } @end
自定义滚动条hwscrollbar:
#import <uikit/uikit.h> @class hwscrollbar; @protocol hwscrollbardelegate <nsobject> //滚动条滑动代理事件 - (void)scrollbardidscroll:(hwscrollbar *)scrollbar; //滚动条点击代理事件 - (void)scrollbartouchaction:(hwscrollbar *)scrollbar; @end @interface hwscrollbar : uiview //背景色 @property (nonatomic, strong) uicolor *backcolor; //前景色 @property (nonatomic, strong) uicolor *forecolor; //滚动动画时长 @property (nonatomic, assign) cgfloat barmoveduration; //限制滚动条最小高度 @property (nonatomic, assign) cgfloat minbarheight; //滚动条实际高度 @property (nonatomic, assign) cgfloat barheight; //滚动条y向位置 @property (nonatomic, assign) cgfloat yposition; //代理 @property (nonatomic, weak) id<hwscrollbardelegate> delegate; @end /*** ---------------分割线--------------- ***/ #import "hwscrollbar.h" #import "uicolor+hw.h" @interface hwscrollbar () @property (nonatomic, weak) uiview *scrollbar; @property (nonatomic, weak) uiview *backview; @end @implementation hwscrollbar - (instancetype)initwithframe:(cgrect)frame { if (self = [super initwithframe:frame]) { //初始化设置 [self initinfo]; //创建控件 [self creatcontrol]; //添加手势 [self addswipegesture]; } return self; } - (void)initinfo { _minbarheight = 40.0f; _barmoveduration = 0.25f; _forecolor = [uicolor colorwithhexstring:@"#2f9cd4"]; _backcolor = [uicolor colorwithhexstring:@"#e6e6e6"]; self.layer.cornerradius = self.bounds.size.width * 0.5; self.layer.maskstobounds = yes; self.backgroundcolor = _backcolor; } - (void)creatcontrol { //背景视图 uiview *backview = [[uiview alloc] initwithframe:self.bounds]; [self addsubview:backview]; _backview = backview; //滚动条 uiview *scrollbar = [[uiview alloc] initwithframe:cgrectmake(0, 0, self.bounds.size.width, self.bounds.size.height)]; scrollbar.backgroundcolor = _forecolor; scrollbar.layer.cornerradius = self.bounds.size.width * 0.5; scrollbar.layer.maskstobounds = yes; [self addsubview:scrollbar]; _scrollbar = scrollbar; } - (void)addswipegesture { //添加点击手势 uitapgesturerecognizer *tap = [[uitapgesturerecognizer alloc] initwithtarget:self action:@selector(handletap:)]; [_backview addgesturerecognizer:tap]; //添加滚动条滑动手势 uipangesturerecognizer *pan = [[uipangesturerecognizer alloc] initwithtarget:self action:@selector(handlepan:)]; [_scrollbar addgesturerecognizer:pan]; } - (void)setforecolor:(uicolor *)forecolor { _forecolor = forecolor; _scrollbar.backgroundcolor = _forecolor; } - (void)setbackcolor:(uicolor *)backcolor { _backcolor = backcolor; self.backgroundcolor = backcolor; } - (void)setbarheight:(cgfloat)barheight { _barheight = barheight > _minbarheight ? barheight : _minbarheight; cgrect temframe = _scrollbar.frame; temframe.size.height = _barheight; _scrollbar.frame = temframe; } - (void)setyposition:(cgfloat)yposition { _yposition = yposition; cgrect temframe = _scrollbar.frame; temframe.origin.y = yposition; _scrollbar.frame = temframe; } - (void)handlepan:(uipangesturerecognizer *)sender { //获取偏移量 cgfloat movey = [sender translationinview:self].y; //重置偏移量,避免下次获取到的是原基础的增量 [sender settranslation:cgpointmake(0, 0) inview:self]; //在顶部上滑或底部下滑直接返回 if ((_yposition <= 0 && movey <= 0) || (_yposition >= self.bounds.size.height - _barheight && movey >= 0)) return; //赋值 self.yposition += movey; //防止瞬间大偏移量滑动影响显示效果 if (_yposition < 0) self.yposition = 0; if (_yposition > self.bounds.size.height - _barheight && movey >= 0) self.yposition = self.bounds.size.height - _barheight; //代理 if (_delegate && [_delegate respondstoselector:@selector(scrollbardidscroll:)]) { [_delegate scrollbardidscroll:self]; } } - (void)handletap:(uitapgesturerecognizer *)sender { //点击滚动条返回 if (sender.view == _scrollbar) return; //获取点击的位置 cgfloat positiony = [sender locationinview:self].y; //赋值 [uiview animatewithduration:_barmoveduration animations:^{ self.yposition = positiony > _yposition ? positiony - _barheight : positiony; }]; //代理 if (_delegate && [_delegate respondstoselector:@selector(scrollbartouchaction:)]) { [_delegate scrollbartouchaction:self]; } } @end
demo 下载链接
猜你喜欢:。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: HTML5实现预览本地图片
下一篇: 春季减肥,知道这些东西让你事半功倍哦!