自定义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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容