自定义YWTabBarViewController
前言
今天是春节前最后一天班了,晚上收拾东西回家。最后一天就好好做件事,写点东西。然后回家过个好年。
自定义YWTabBarViewController
先看最终效果图:
tabBarController.gif首先需要明了的是APP一启动便有下面的tabBar,所以tabBarController得在整个APP的rootViewController创建添加。
所以,我们创建了我们项目的RootViewController,并设置为整个APP的rootViewController。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RootViewController *rootVC = [[RootViewController alloc] init];
UINavigationController *rootNavC = [[UINavigationController alloc] initWithRootViewController:rootVC];
self.window.rootViewController = rootNavC;
return YES;
}
我们再来看看RootViewController里:
可以看到我们通过我们自定义的YWTabBarViewController类,将tabBarController这个容器里视图添加到了RootViewController上,并设置默认选中第一个按钮。而且还实现了tabBarController的代理方法。
#import "RootViewController.h"
#import "YWTabBarViewController.h"
#import "AAAViewController.h"
#import "BBBViewController.h"
#import "CCCViewController.h"
@interface RootViewController ()<YWTabBarControllerDelegate>
{
YWTabBarViewController *_tabBarVC;
}
@property (nonatomic, strong)NSArray *vcsArr;
@end
@implementation RootViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = @"root";
self.navigationController.navigationBar.hidden = YES;
_tabBarVC = [[YWTabBarViewController alloc] initWithViewControllers:[self vcsArr] imagesArr:[self imagesArr]];
_tabBarVC.selectedIndex = 0;
_tabBarVC.delegate = self;
[self.view addSubview:_tabBarVC.view];
}
#pragma mark ---- private method
- (NSMutableArray *)imagesArr
{
NSArray *imageNames=[NSArray arrayWithObjects:
[NSArray arrayWithObjects:@"bottomBar_home.png",@"bottomBar_home1.png",@"bottomBar_home1.png", nil],
[NSArray arrayWithObjects:@"bottomBar_agency.png",@"bottomBar_agency1.png",@"bottomBar_agency1.png", nil],
[NSArray arrayWithObjects:@"bottomBar_act.png",@"bottomBar_act1.png",@"bottomBar_act1.png", nil] ,nil];
NSMutableArray *imgArr=[[NSMutableArray alloc]init];
for (int i=0; i<imageNames.count; i++)
{
NSArray *names=[imageNames objectAtIndex:i];
NSMutableDictionary *imgDic= [NSMutableDictionary dictionaryWithCapacity:3];
[imgDic setObject:[UIImage imageNamed:[names objectAtIndex:0]] forKey:@"Default" ];
[imgDic setObject:[UIImage imageNamed:[names objectAtIndex:1]] forKey:@"Highlighted"];
[imgDic setObject:[UIImage imageNamed:[names objectAtIndex:2]] forKey:@"Seleted"];
[imgArr addObject:imgDic];
}
return imgArr;
}
- (NSArray *)vcsArr
{
AAAViewController *aaaVC = [[AAAViewController alloc] init];
UINavigationController *navCAAA = [[UINavigationController alloc] initWithRootViewController:aaaVC];
BBBViewController *bbbVC = [[BBBViewController alloc] init];
UINavigationController *navCBBB = [[UINavigationController alloc] initWithRootViewController:bbbVC];
CCCViewController *cccVC = [[CCCViewController alloc] init];
UINavigationController *navCCCC = [[UINavigationController alloc] initWithRootViewController:cccVC];
return @[navCAAA, navCBBB, navCCCC];
}
#pragma mark ---- tabBarController delegate
- (BOOL)ywtabBarViewController:(YWTabBarViewController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
for(UINavigationController *navC in [self vcsArr])
{
if(navC != viewController){
[navC popToRootViewControllerAnimated:YES];
}else{
if(navC.viewControllers.count>0){
}
}
}
return YES;
}
- (void)ywtabBarViewController:(YWTabBarViewController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSLog(@"xxxx");
}
@end
然后我们再看看YWTabBarViewController究竟是怎样写的:
** YWTabBarViewController.h **
#import <UIKit/UIKit.h>
@class YWTabBarViewController;
@protocol YWTabBarControllerDelegate <NSObject>
- (BOOL)ywtabBarViewController:(YWTabBarViewController *)tabBarController shouldSelectViewController:(UIViewController *)viewController;
- (void)ywtabBarViewController:(YWTabBarViewController *)tabBarController didSelectViewController:(UIViewController *)viewController;
@end
@interface YWTabBarViewController : UIViewController
@property (nonatomic, assign)NSUInteger selectedIndex;
@property (nonatomic, weak)id<YWTabBarControllerDelegate> delegate;
- (instancetype)initWithViewControllers:(NSArray *)vcsArr imagesArr:(NSArray *)imgsArr;
@end
** YWTabBarViewController.m **
代码里写了很多注释,基本很清楚了。但是还有几点重要部分仍需要说一下。
1.自定义东西,首先要面临的就是写初始化方法。初始化方法应该写得使调用者做尽量少的事,让调用者感到清晰明了,且简单易用。
2.其实tabBarController最重要的逻辑就是切换下面tabBar时,tabBar的按钮状态和_mainView视图同时做相应的切换。在代码里由- (void)displayViewAtIndex:(NSUInteger)index
完成这个逻辑。
3.tabBarController暴露给外部一个selectedIndex属性,外部传入该属性参数,tabBarControler内部由selectedIndex的值来判断按钮状态及相应的_mainView。我们重写了selectedIndex的setter方法,只要我们在外部_tabBarVC.selectedIndex = 1;
便会调用内部方法,完成tabBar按钮状态的改变和相应视图的显示。
#import "YWTabBarViewController.h"
#import "YWTabBar.h"
@interface YWTabBarViewController ()<YWTabBarDelegate>
{
NSArray *_vcsArr;
NSArray *_imgsArr;
UIView *_containView;
UIView *_mainView;
YWTabBar *_ywTabBar;
}
@end
@implementation YWTabBarViewController
#pragma mark ---- life cycle
- (instancetype)initWithViewControllers:(NSArray *)vcsArr imagesArr:(NSArray *)imgsArr
{
self = [super init];
if(self)
{
_vcsArr = [NSArray arrayWithArray:vcsArr]; // tabBarController容器类里的viewController们
_imgsArr = [NSArray arrayWithArray:imgsArr]; // 各个viewController的图片资源
CGRect rect = [UIScreen mainScreen].bounds;
_containView = [[UIView alloc] initWithFrame:rect];
self.view = _containView; // 占满整个屏幕
_mainView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, MainScreen_W, MainScreen_H-TabBar_H)];
_mainView.backgroundColor = [UIColor groupTableViewBackgroundColor];
[_containView addSubview:_mainView];
_ywTabBar = [[YWTabBar alloc] initWithFrame:CGRectMake(0, MainScreen_H-TabBar_H, MainScreen_W, TabBar_H)
imgagesArray:imgsArr];
_ywTabBar.backgroundColor = [UIColor whiteColor];
_ywTabBar.delegate = self;
[_containView addSubview:_ywTabBar];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
#pragma mark ---- tabBar delegate
- (void)ywtabBar:(YWTabBar *)tabBar didSelectIndex:(NSUInteger)index
{
[self displayViewAtIndex:index];
}
#pragma mark ---- function method
// 显示响应的viewController
// (重点)控制当tabBar点击时显示相应的viewController的view
- (void)displayViewAtIndex:(NSUInteger)index
{
if([_delegate respondsToSelector:@selector(ywtabBarViewController:shouldSelectViewController:)])
{
if(![_delegate ywtabBarViewController:self shouldSelectViewController:_vcsArr[index]])
{
[_ywTabBar tabBarSelectAtIndex:index]; // 切换tabBar按钮的高亮状态
}
}
//--------- 根据点击tabBar的index来相应的显示视图
_selectedIndex = index;
UIViewController *selectedVC = _vcsArr[index];
selectedVC.view.frame = _mainView.frame;
// 每切换一个vc,便将其view添加在_mainView上,等vc都被切换过了其实它们都已被添加在_mainView上。然后再次切换某个vc,实际上就是将其view移动到最上层。
if([selectedVC.view isDescendantOfView:_mainView]) // 判断selectedVC.view是否是_mainView上的视图
{
[_mainView bringSubviewToFront:selectedVC.view];
}else{
[_mainView addSubview:selectedVC.view];
}
//---------
// 并调用YWTabBarViewController的代理方法,在RootViewController实现时做些其他处理。
if([_delegate respondsToSelector:@selector(ywtabBarViewController:didSelectViewController:)])
{
[_delegate ywtabBarViewController:self didSelectViewController:_vcsArr[index]];
}
}
#pragma mark ---- setter/getter
// 重写selectedIndex属性的setter方法,在内控制切换时tabBar按钮的状态和相应视图的显示。
- (void)setSelectedIndex:(NSUInteger)selectedIndex
{
[_ywTabBar tabBarSelectAtIndex:selectedIndex];
[self displayViewAtIndex:selectedIndex];
}
@end
我们是把tabBar独立建了类YWTabBar,在其内部完成了视图布局,逻辑处理等。我们来看看YWTabBar是怎样写的:
** YWTabBar.h **
#import <UIKit/UIKit.h>
@class YWTabBar;
@protocol YWTabBarDelegate <NSObject>
- (void)ywtabBar:(YWTabBar *)tabBar didSelectIndex:(NSUInteger)index;
@end
@interface YWTabBar : UIView
@property (nonatomic, weak)id<YWTabBarDelegate> delegate;
- (instancetype)initWithFrame:(CGRect)frame imgagesArray:(NSArray *)imgsArr;
// 只是改变按钮的状态
- (void)tabBarSelectAtIndex:(NSUInteger)selectIndex;
@end
** YWTabBar.m **
#import "YWTabBar.h"
@interface YWTabBar ()
{
CGRect _frame;
NSArray *_imgsArr;
NSMutableArray *_btnsArr;
UIImageView *_backgroudImage;
NSUInteger _selectedIndex;
}
@end
@implementation YWTabBar
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
// 对于系统的初始化方法,重写但置空
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame imgagesArray:(NSArray *)imgsArr
{
self = [super initWithFrame:frame];
if(self)
{
_frame = frame;
_imgsArr = [NSArray arrayWithArray:imgsArr];
_selectedIndex = 0;
_btnsArr = [[NSMutableArray alloc] init];
[self loadContentView];
}
return self;
}
// tabBar的视图布局
- (void)loadContentView
{
_backgroudImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, MainScreen_W, TabBar_H)];
_backgroudImage.userInteractionEnabled = YES;
[self addSubview:_backgroudImage];
for(int i=0; i<_imgsArr.count; i++)
{
NSDictionary *dict = _imgsArr[i];
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(_frame.size.width/_imgsArr.count*i , 0, _frame.size.width/_imgsArr.count, TabBar_H)];
[btn setImage:dict[@"Default"] forState:UIControlStateNormal];
[btn setImage:dict[@"Highlighted"] forState:UIControlStateHighlighted];
[btn setImage:dict[@"Seleted"] forState:UIControlStateSelected];
[btn setBackgroundColor:[UIColor greenColor]];
btn.userInteractionEnabled = YES;
btn.showsTouchWhenHighlighted = YES;
btn.tag = i;
[btn addTarget:self action:@selector(tabBarBtnClick:) forControlEvents:UIControlEventTouchUpInside];
[_backgroudImage addSubview:btn];
[_btnsArr addObject:btn];
}
}
#pragma mark ---- event response
- (void)tabBarBtnClick:(UIButton *)btn
{
NSUInteger tag = btn.tag;
[self tabBarSelectAtIndex:tag]; // tabBar的btn状态变化
if([self.delegate respondsToSelector:@selector(ywtabBar:didSelectIndex:)])
{
[self.delegate ywtabBar:self didSelectIndex:tag]; // 对于相应视图的切换,则由代理完成。即YWTabBarViewController里的displayViewAtIndex:
}
}
#pragma mark ---- function method
// 只是改变按钮的状态
- (void)tabBarSelectAtIndex:(NSUInteger)selectIndex
{
for(int i=0; i<_btnsArr.count; i++)
{
UIButton *btn = _btnsArr[i];
btn.selected = NO;
btn.userInteractionEnabled = YES;
if(i == selectIndex){
btn.selected = YES;
btn.userInteractionEnabled = NO;
}
}
}
@end