1.写在前面:
前段时间项目用到了类似新闻类客户端顶部的滚动条,虽然网上也有很多,但是本着锻炼自己的想法,自己动手写了一个,所幸效果不差,遂分享出来,一方面给大家参考,一方面也给自己记录。
上效果图:
2.使用方法:
方法1:
NSArray *titleArray = @[@"推荐",@"热点",@"视频",@"体育",@"搞笑"];
NSMutableArray *vcArray = @[].mutableCopy;
for (int i = 0; i<titleArray.count; i++) {
ViewController *vc = [ViewController new];
[vcArray addObject:vc];
}
XBYTopTabBarViewController *tabVC = [[XBYTopTabBarViewController alloc] initWithSegmentTitles:titleArray childVcs:vcArray];
[self.navigationController pushViewController:tabVC animated:YES];
方法2:(推荐使用)
//新建ViewController继承XBYTopTabBarViewController,改写init方法
- (instancetype)init {
if (self = [super init]) {
NSArray *tabNames = @[@"推荐",@"热点",@"视频",@"体育",@"搞笑"];
NSMutableArray *vcs = @[].mutableCopy;
for (NSString *name in tabNames) {
ViewController *vc = [[ViewController alloc]init];
[vcs addObject:vc];
}
self = [super initWithSegmentTitles:tabNames childVcs:vcs];
}
return self;
}
这里是封装的是一个ViewController,网上很多封装的都是view,需要一系列配置,我觉得比较麻烦,还是继承来的快一些,继承XBYTopTabBarViewController以后,有多少个子项新建多个vc,调用
- (instancetype)initWithSegmentTitles:(NSArray *)titles
childVcs:(NSArray *)childVcs;
方法,这里继承的vc和众多子vc的生命周期都已经设置好了,可以不用管了,只需要继承(懒一点继承也不要了,直接调用上面这个方法),其他的都不用管了。
3.实现思路:
这里用到了两个scrollView
上面title一栏是一个scrollView(这里也可以用其他view,这个scrollView不需要左右滑动,但是需要响应点击事件),后面用smallScrollView,下面可以左右滑动的部分也是一个scrollView,后面用bigScrollView表示。
有多少个滑动项,就新建多少个label添加到smallScrollView里面:
- (void)setupSmallScrollView {
CGFloat labelW = kScreenWidth/(self.segmentTitles.count>5?5:self.segmentTitles.count);
self.smallScrollView.contentSize = CGSizeMake(labelW * self.segmentTitles.count, 44);
CGFloat labelX,labelY = 0, labelH = smallScrollViewH;
for (int i = 0; i < self.segmentTitles.count; i++) {
labelX = i * labelW;
GGSOrderStatusLabel *label = [[GGSOrderStatusLabel alloc]initWithFrame:CGRectMake(labelX, labelY, labelW, labelH)];
label.text = self.segmentTitles[i];
label.tag = i;
label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(labelTapAction:)]];
[self.smallScrollView addSubview:label];
}
}
给label添加手势,响应点击事件,同时让bigScrollView跟随点击的smallScrollView的子项联动:
- (void)labelTapAction:(UITapGestureRecognizer *)gesture {
GGSOrderStatusLabel *titlelable = (GGSOrderStatusLabel *)gesture.view;
if (titlelable.tag == _currentIndex) {
return;
}
CGFloat offsetX = titlelable.tag * self.bigScrollView.frame.size.width;
CGFloat offsetY = self.bigScrollView.contentOffset.y;
CGPoint offset = CGPointMake(offsetX, offsetY);
[self.bigScrollView setContentOffset:offset animated:YES]; //点击smallScrollView的label项,bigScrollView要联动,同时滚动。
}
实现bigScrollView的代理,让smallScrollView跟随它联动:
/** 滚动结束后调用 */
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
// 获得索引
NSUInteger index = scrollView.contentOffset.x / self.bigScrollView.frame.size.width;
// 滚动标题栏
GGSOrderStatusLabel *titleLable = (GGSOrderStatusLabel *)self.smallScrollView.subviews[index];
// label居中的offsetx
CGFloat offsetx = titleLable.center.x - self.smallScrollView.frame.size.width * 0.5;
CGFloat offsetMax = self.smallScrollView.contentSize.width - self.smallScrollView.frame.size.width;
if (offsetx < 0) {
offsetx = 0;
}else if (offsetx > offsetMax){
offsetx = offsetMax;
}
CGPoint offset = CGPointMake(offsetx, self.smallScrollView.contentOffset.y);
// 要放在gcd里才有动画
dispatch_async(GCD_MAINQUEUE, ^{
[self.smallScrollView setContentOffset:offset animated:YES];
});
UIViewController *oldVC = nil;
if (_currentIndex != -1) {
oldVC = self.childVcs[_currentIndex];
}
UIViewController *newsVc = self.childVcs[index];
self.currentIndex = index;
// 其他label设置成初始状态
[self.smallScrollView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (idx != index) {
GGSOrderStatusLabel *temlabel = self.smallScrollView.subviews[idx];
temlabel.scale = 0.0;
}
}];
if (newsVc.view.superview) {//调用生命周期函数(如viewwillappear等)
if (oldVC) {
[newsVc beginAppearanceTransition:NO animated:YES];
[newsVc endAppearanceTransition];
}
[newsVc beginAppearanceTransition:YES animated:YES];
[newsVc endAppearanceTransition];
return;
}
[self addChildViewController:newsVc];
newsVc.view.frame = scrollView.bounds;//bounds的x就是scrollView的offsetx
[self.bigScrollView addSubview:newsVc.view];
[newsVc didMoveToParentViewController:self];
if ([self.delegate respondsToSelector:@selector(segmentViewController:didAddChildViewController:)]) {
[self.delegate segmentViewController:self didAddChildViewController:newsVc];
}
}
3.其他
见下面头文件描述:
@protocol XBYTopTabBarViewControllerDelegate <NSObject>
@optional
- (void)topTabBarViewController:(XBYTopTabBarViewController *)topTabBarVC
didAddChildViewController:(UIViewController *)childVC;
@end
@interface XBYTopTabBarViewController : UIViewController
/**
上面滑块的标题
*/
@property (nonatomic, strong, readonly) NSArray *segmentTitles;
/**
一个滑块对应一个viewController
*/
@property (nonatomic, strong, readonly) NSMutableArray *childVcs;
/**
当前滑块的index
*/
@property (nonatomic, assign) NSInteger currentIndex;
/**
用于存放滑块的scrollView
*/
@property (nonatomic, strong) UIScrollView *smallScrollView;
/**
用于存放viewController的scrollView
*/
@property (nonatomic, strong) UIScrollView *bigScrollView;
@property (nonatomic, weak) id<XBYTopTabBarViewControllerDelegate> delegate;
/**
<#Description#>
@param titles 滑块标题数组
@param childVcs 滑块对应的viewController数组
@return <#return value description#>
*/
- (instancetype)initWithSegmentTitles:(NSArray *)titles
childVcs:(NSArray *)childVcs;