类似于下图这样,点咨询,热点,博客,推荐下面的页面会切换到相应的页面,左右滑动页面的时候顶部到tabbar会做出相应反应,一个item淡出,另一个淡入,这里是颜色和大小的动态的改变。
先说一下这个tabbar是怎么实现的。
其实就是一个UIScrollView
#import <UIKit/UIKit.h>
@interface TitleBarView : UIScrollView
@property (nonatomic, strong) NSMutableArray *titleButtons;
@property (nonatomic, assign) NSUInteger currentIndex;
@property (nonatomic, copy) void (^titleButtonClicked)(NSUInteger index);
- (instancetype)initWithFrame:(CGRect)frame andTitles:(NSArray*)titles;
@end
#import "TitleBarView.h"
#import "UIColor+Util.h"
@interface TitleBarView ()
@end
@implementation TitleBarView
- (instancetype)initWithFrame:(CGRect)frame andTitles:(NSArray *)titles
{
self = [super initWithFrame:frame];
if (self) {
_currentIndex = 0;
_titleButtons = [NSMutableArray new];
CGFloat buttonWidth = frame.size.width / titles.count;
CGFloat buttonHeight = frame.size.height;
[titles enumerateObjectsUsingBlock:^(NSString *title, NSUInteger idx, BOOL *stop) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.backgroundColor = [UIColor colorWithHex:0xE1E1E1];
button.titleLabel.font = [UIFont systemFontOfSize:15];
[button setTitleColor:[UIColor colorWithHex:0x909090] forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateNormal];
button.frame = CGRectMake(buttonWidth * idx, 0, buttonWidth, buttonHeight);
button.tag = idx;
[button addTarget:self action:@selector(onClick:) forControlEvents:UIControlEventTouchUpInside];
[_titleButtons addObject:button];
[self addSubview:button];
//[self sendSubviewToBack:button];
}];
//他这里估计默认tabbar的item项数不会太多,这里有四个,所以设置了contentSize和屏幕宽度一样宽,也就不能左右滑动了
self.contentSize = CGSizeMake(frame.size.width, 25);
self.showsHorizontalScrollIndicator = NO;
UIButton *firstTitle = _titleButtons[0];
[firstTitle setTitleColor:[UIColor colorWithHex:0x009000] forState:UIControlStateNormal];
firstTitle.transform = CGAffineTransformMakeScale(1.15, 1.15);//这里直观的展示就是字体变大
}
return self;
}
- (void)onClick:(UIButton *)button
{
if (_currentIndex != button.tag) {
UIButton *preTitle = _titleButtons[_currentIndex];
[preTitle setTitleColor:[UIColor colorWithHex:0x909090] forState:UIControlStateNormal];
preTitle.transform = CGAffineTransformIdentity;
[button setTitleColor:[UIColor colorWithHex:0x009000] forState:UIControlStateNormal];
button.transform = CGAffineTransformMakeScale(1.2, 1.2);
_currentIndex = button.tag;
_titleButtonClicked(button.tag);//要在外面设置这个回调函数,当点击不同的选项的时候,下面那个展示页面展示不同的东西。
}
}
我们再来看看下面的那个展示页面是怎么弄的。他这里用的是UITableViewController,感觉很新奇的样子。其实用UIScrollView实现是一样的。
#import <UIKit/UIKit.h>
@interface HorizonalTableViewController : UITableViewController
@property (nonatomic, strong) NSArray *controllers;
@property (nonatomic, copy) void (^changeIndex)(NSUInteger index);//回调函数,当左右滑动不同页面的时候调用,通知那个tabbar做出相应的反应
@property (nonatomic, copy) void (^scrollView)(CGFloat offsetRatio, NSUInteger focusIndex, NSUInteger animationIndex);
@property (nonatomic, copy) void (^viewDidAppear)(NSInteger index);
- (instancetype)initWithViewControllers:(NSArray *)controllers;
- (void)scrollToViewAtIndex:(NSUInteger)index;
@end
#import "HorizonalTableViewController.h"
#import "Utils.h"
@interface HorizonalTableViewController ()
@end
static NSString *kHorizonalCellID = @"HorizonalCell";
@implementation HorizonalTableViewController
- (instancetype)initWithViewControllers:(NSArray *)controllers
{
self = [super init];
if (self) {
_controllers = controllers;
for (UIViewController *controller in controllers) {
[self addChildViewController:controller];
}
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
/***** 为解决iPhone 6 下的popviewcontroller后的问题而做的无奈之举,这样会引入新的问题,very ugly,亟待解决 *****/
self.tableView = [UITableView new];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.scrollsToTop = NO;
self.tableView.transform = CGAffineTransformMakeRotation(-M_PI_2);//逆时针旋转90度
self.tableView.showsVerticalScrollIndicator = NO;
self.tableView.pagingEnabled = YES;//分页
self.tableView.backgroundColor = [UIColor themeColor];
self.tableView.bounces = NO;//到tableView的顶部和底部的时候禁止继续滑动
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kHorizonalCellID];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _controllers.count;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return tableView.frame.size.width;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kHorizonalCellID forIndexPath:indexPath];
cell.contentView.transform = CGAffineTransformMakeRotation(M_PI_2);//顺时针旋转90度
cell.contentView.backgroundColor = [UIColor themeColor];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UIViewController *controller = _controllers[indexPath.row];
controller.view.frame = cell.contentView.bounds;
[cell.contentView addSubview:controller.view];
return cell;
}
#pragma mark - <UIScrollViewDelegate>
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
//NSLog(@"scrollViewDidEndDecelerating");
[self scrollStop:YES];//当拖动这个整个动作结束的时候调用
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//NSLog(@"scrollViewDidScroll");
[self scrollStop:NO];//当拖拽结束的时候,一次拖拽这个方法会调用很多次,可以实时得到拖拽的位移,然后通知tabbar做出连续的相应的改变
}
#pragma mark -
- (void)scrollToViewAtIndex:(NSUInteger)index
{
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]
atScrollPosition:UITableViewScrollPositionNone
animated:NO];
if (_viewDidAppear) {
_viewDidAppear(index);
}
}
- (void)scrollStop:(BOOL)didScrollStop
{
CGFloat horizonalOffset = self.tableView.contentOffset.y;
CGFloat screenWidth = self.tableView.frame.size.width;
CGFloat offsetRatio = (NSUInteger)horizonalOffset % (NSUInteger)screenWidth / screenWidth;
NSUInteger focusIndex = (horizonalOffset + screenWidth / 2) / screenWidth;
//NSLog(@"scrollStop");
if (horizonalOffset != focusIndex * screenWidth) {//判断是页面切换还是他的子controller里面的tableView的滑动
NSUInteger animationIndex = horizonalOffset > focusIndex * screenWidth ? focusIndex + 1: focusIndex - 1;
if (focusIndex > animationIndex) {offsetRatio = 1 - offsetRatio;}
_scrollView(offsetRatio, focusIndex, animationIndex);//focusIndex是当前的index,animationIndex是要前往的index
}
if (didScrollStop) {
/*
[_controllers enumerateObjectsUsingBlock:^(UIViewController *vc, NSUInteger idx, BOOL *stop) {
if ([vc isKindOfClass:[UITableViewController class]]) {
((UITableViewController *)vc).tableView.scrollsToTop = (idx == focusIndex);
}
}];
*/
_changeIndex(focusIndex);
}
}
再来看看主控制器里是怎么调用上面两个东西的:
CGFloat titleBarHeight = 36;
_titleBar = [[TitleBarView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, titleBarHeight) andTitles:subTitles];
_titleBar.backgroundColor = [UIColor clearColor];
[self.view addSubview:_titleBar];
_viewPager = [[HorizonalTableViewController alloc] initWithViewControllers:controllers];
CGFloat height = self.view.bounds.size.height - titleBarHeight - 64 - (underTabbar ? 49 : 0);
_viewPager.view.frame = CGRectMake(0, titleBarHeight, self.view.bounds.size.width, height);
[self addChildViewController:self.viewPager];
[self.view addSubview:_viewPager.view];
__weak TitleBarView *weakTitleBar = _titleBar;
__weak HorizonalTableViewController *weakViewPager = _viewPager;
_viewPager.changeIndex = ^(NSUInteger index) {
weakTitleBar.currentIndex = index;
for (UIButton *button in weakTitleBar.titleButtons) {
if (button.tag != index) {
[button setTitleColor:[UIColor colorWithHex:0x909090] forState:UIControlStateNormal];
button.transform = CGAffineTransformIdentity;
} else {
[button setTitleColor:[UIColor colorWithHex:0x009000] forState:UIControlStateNormal];
button.transform = CGAffineTransformMakeScale(1.2, 1.2);
}
}
[weakViewPager scrollToViewAtIndex:index];
};
_viewPager.scrollView = ^(CGFloat offsetRatio, NSUInteger focusIndex, NSUInteger animationIndex) {
UIButton *titleFrom = weakTitleBar.titleButtons[animationIndex];
UIButton *titleTo = weakTitleBar.titleButtons[focusIndex];
CGFloat colorValue = (CGFloat)0x90 / (CGFloat)0xFF;
[UIView transitionWithView:titleFrom duration:0.1 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[titleFrom setTitleColor:[UIColor colorWithRed:colorValue*(1-offsetRatio) green:colorValue blue:colorValue*(1-offsetRatio) alpha:1.0]
forState:UIControlStateNormal];
titleFrom.transform = CGAffineTransformMakeScale(1 + 0.2 * offsetRatio, 1 + 0.2 * offsetRatio);
} completion:nil];
[UIView transitionWithView:titleTo duration:0.1 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[titleTo setTitleColor:[UIColor colorWithRed:colorValue*offsetRatio green:colorValue blue:colorValue*offsetRatio alpha:1.0]
forState:UIControlStateNormal];
titleTo.transform = CGAffineTransformMakeScale(1 + 0.2 * (1-offsetRatio), 1 + 0.2 * (1-offsetRatio));
} completion:nil];
};
_titleBar.titleButtonClicked = ^(NSUInteger index) {
[weakViewPager scrollToViewAtIndex:index];
};
- (void)scrollToViewAtIndex:(NSUInteger)index
{
_viewPager.changeIndex(index);
}
在这里
_viewPager.scrollView
当页面切换的时候时时动态的改变tabbar的状态。
这里我把这些代码单独拿出来做了个简单的demo: