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

iOS自定义可展示、交互的scrollView滚动条

程序员文章站 2023-08-17 21:22:40
上一篇简述了封装,本篇在此基础上添加了一个自定义的scrollview滚动条,可展示、交互,首先看一下效果图: 简单阐述一下实现逻辑:自定义滚动条视图继承uiview...

上一篇简述了封装,本篇在此基础上添加了一个自定义的scrollview滚动条,可展示、交互,首先看一下效果图:

iOS自定义可展示、交互的scrollView滚动条

简单阐述一下实现逻辑:自定义滚动条视图继承uiview,添加滚动条滑动事件、其他区域点击事件,通过代理方法与列表关联。在列表刷新完成及scrollview代理方法中更新滚动条。

iOS自定义可展示、交互的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 下载链接

猜你喜欢:。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。