在iOS原生的tabBar中傀顾,能夠?qū)崿F(xiàn)按鈕的點擊事件促王,能夠?qū)崿F(xiàn)視圖控制器的切換等,但是在實際工程中螃征,對于tabBar的要求的功能往往是系統(tǒng)自己實現(xiàn)不了的,所以我們這里就需要用到自定義的tabBar了透敌。
對于tabBar上展示視圖控制器盯滚,我們會采用的是在把幾個視圖控制直接加載到tabBarController上去。這里新建三個視圖控制器酗电,由于在
一魄藕、系統(tǒng)樣式
ViewController會有其他代碼,所以我們這里另一寫一個類撵术,在這里只設(shè)置一個背景顏色就可以了背率。所以我們先新建一個類叫做WJViewController,讓它繼承自UIViewController嫩与。這里設(shè)置視圖的背景顏色寝姿,這里可以設(shè)置為隨機色。
// 設(shè)置背景顏色為隨機色
self.view.backgroundColor = [UIColor colorWithRed:arc4random() % 256 /255.0 green:arc4random() % 256 /255.0 blue:arc4random() % 256 /255.0 alpha:1.0];
然后新建三個視圖控制器划滋,繼承自WJViewController饵筑,這樣三個視圖控制的背景顏色都有了。新建的三個類古毛,分別命名為WJFirstViewController翻翩、WJSecondViewController都许、WJThirdViewController。然后我們?nèi)崿F(xiàn)相關(guān)方法嫂冻。
1.首先創(chuàng)建一個tabBarController胶征,用于接收實例化好的視圖控制器。
// 1.創(chuàng)建標(biāo)簽欄控制器 UITabBarController *tabBarController = [[UITabBarController alloc]init];
2.然后就可創(chuàng)建需要讓標(biāo)簽欄控制器管理的子視圖控制器:
// 1>.第一個視圖控制器
WJFirstViewController *first = [[WJFirstViewController alloc]init];
first.tabBarItem.title = @"first";
first.tabBarItem.image = [[UIImage imageNamed:@"tiaoman_u.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
first.tabBarItem.selectedImage = [[UIImage imageNamed:@"tiaoman_d.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
// 2>.第二個視圖控制器
WJSecondViewController *second = [[WJSecondViewController alloc]init];
second.tabBarItem.title = @"second";
second.tabBarItem.image = [[UIImage imageNamed:@"faxian_u.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
second.tabBarItem.selectedImage = [[UIImage imageNamed:@"faxian_d.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
// 3>.第三個視圖控制器
WJThirdViewController *third = [[WJThirdViewController alloc]init];
third.tabBarItem.title = @"third";
third.tabBarItem.image = [[UIImage imageNamed:@"wode_u.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
third.tabBarItem.selectedImage = [[UIImage imageNamed:@"wode_d.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
3.對子視圖控制器進(jìn)行管理
有兩種方案可以選擇:一是用數(shù)組進(jìn)行接收桨仿,二是用一個方法進(jìn)行接收子視圖控制器睛低。
法一:
//tabBarController.viewControllers = @[first, second, third];
法二:
[tabBarController addChildViewController:first];
[tabBarController addChildViewController:second];
[tabBarController addChildViewController:third];
這里最好使用第二種方法進(jìn)行對子視圖控制器進(jìn)行管理。
代碼進(jìn)行到這里就可以把視圖控制器tabBarController展示在Windows上了服傍,所以需要把tabBarController設(shè)為根視圖钱雷。
// 可以通過每個已經(jīng)顯示在界面上的視圖,拿到當(dāng)前應(yīng)用程序的window
self.view.window.rootViewController = tabBarController;
}
這樣代碼進(jìn)行到這里系統(tǒng)樣式的功能就已經(jīng)實現(xiàn)了,但是對于我們實際要求的功能相差甚遠(yuǎn)吹零,所以我們開始自定義的tabBar的創(chuàng)建罩抗。
二、自定義樣式
下面我們來簡單分析下系統(tǒng)的tabBar的功能灿椅。
2.1分析
分析首先按鈕有按鈕點擊事件套蒂,點擊按鈕后會改變相應(yīng)的視圖控制器,而且點擊按鈕后的圖標(biāo)會有相應(yīng)的改變茫蛹。而自定義的tabBar也需要達(dá)到這些功能操刀,甚至還需要達(dá)到自定義按鈕的形狀的和功能的要求。
對于系統(tǒng)的tabBar來說婴洼,能夠展示按鈕的文字和圖片骨坑,而這些內(nèi)容是加載在tabBar的_UITabBarBackgroundView上的UITabBarButton上的UILabel和UIImageView上的。而我們要自定義按鈕就需要覆蓋系統(tǒng)本身的控件柬采,但這些控件我們是拿不到的欢唾,用點語法這些控件是不能聯(lián)想出來的,但我們又要實現(xiàn)和系統(tǒng)一樣的功能警没,我們只能用一個方法把tabBar上的所有控件移除匈辱,因此要用遍歷的方法實現(xiàn)移除;我們移除了系統(tǒng)自帶的tabBar后就需要自定義一個wjTabBar來代替原有的tabBar杀迹。然后在wjTabBar上添加按鈕亡脸,然后設(shè)置相應(yīng)的點擊事件,更改相應(yīng)的狀態(tài)就可實現(xiàn)和系統(tǒng)一樣的功能树酪。
// 聲明一個wjTabBar的全局變量
@property (nonatomic, strong) WJTabBar *wj_tabBar;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// 2.刪除自動創(chuàng)建的tabBarButton
for (UIView *view in self.tabBar.subviews) {
// 打印tabBar上所有控件
NSLog(@"%@",self.tabBar.subviews);
// 移除tabBar上所有的子控件
[view removeFromSuperview];
}
// 把self.wj_tabBar添加到視圖上
[self.tabBar addSubview:self.wj_tabBar];
}
對wj_tabBar設(shè)置懶加載
#pragma mark - 懶加載
- (WJTabBar *)wj_tabBar {
if (!_wj_tabBar) {
// 創(chuàng)建wj_tabBar
_wj_tabBar = [[WJTabBar alloc]init];
// 設(shè)置frame
_wj_tabBar.frame = self.tabBar.bounds;
// 設(shè)置一個背景顏色
_wj_tabBar.backgroundColor = [UIColor cyanColor];
}
return _wj_tabBar;
}
2.2現(xiàn)在分析下按鈕的創(chuàng)建:
按鈕的創(chuàng)建應(yīng)該視圖控制器創(chuàng)建有關(guān)浅碾,所以在設(shè)計方法的時候應(yīng)該傳一個視圖控制器的參數(shù);按鈕還應(yīng)該設(shè)置文字续语,選中時的圖片和普通狀態(tài)下的圖片垂谢。
2.2.1把tabBar展示到視圖上
考慮到后期調(diào)用的方便,可以在.h文件中暴露出接口疮茄,以供使用滥朱;所以在.h文件中設(shè)置方法根暑,然后在.m文件實現(xiàn)相關(guān)的方法。
#pragma mark - 添加子視圖控制器
- (void)addController:(UIViewController *)controller withTitle:(NSString *)title imageName:(NSString *)imageName selectedImageName:(NSString *)selectedImageName {
// 設(shè)置tabBarItem的子視圖
controller.tabBarItem.title = title;
controller.tabBarItem.image = [[UIImage imageNamed:imageName] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
controller.tabBarItem.selectedImage = [[UIImage imageNamed:selectedImageName] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
// 將視圖控制器添加到標(biāo)簽欄控制器中
[self addChildViewController:controller];
// 可以讓自己定制的tabBar去創(chuàng)建一個對應(yīng)的按鈕
[self.wj_tabBar addButtonWithTabBarItem:controller.tabBarItem];
/* 測試的
static int i = 0;
if (i == 0) {
WJTabBarButton *button = [[WJTabBarButton alloc]initWithFrame:CGRectMake(10, 0, 45, 45)];
button.item = controller.tabBarItem;
button.normalColor = [UIColor blueColor];
button.selectedColor = [UIColor greenColor];
[button addTarget:self action:@selector(firstClick:)];
[self.wj_tabBar addSubview:button];
i = 1;
}
*/
}
然后去ViewControllers去添加視圖控制器到tabBar上
#pragma mark - 創(chuàng)建視圖控制器
- (void)wj_creatControll {
// 1.創(chuàng)建標(biāo)簽欄控制器
WJTabBarController *tabBarController = [[WJTabBarController alloc]init];
WJFirstViewController *first = [[WJFirstViewController alloc]init];
[tabBarController addController:first withTitle:@"first" imageName:@"tiaoman_u.png" selectedImageName:@"tiaoman_d.png"];
WJSecondViewController *second = [[WJSecondViewController alloc]init];
[tabBarController addController:second withTitle:@"second" imageName:@"faxian_u.png" selectedImageName:@"faxian_d.png"];
//WJThirdViewController *third = [[WJThirdViewController alloc]init];
//[tabBarController addController:third withTitle:@"third" imageName:@"wode_u.png" selectedImageName:@"wode_d.png"];
// 可以通過每個已經(jīng)顯示在界面上的視圖,拿到當(dāng)前應(yīng)用程序的window
self.view.window.rootViewController = tabBarController;
// 設(shè)置第幾個被選中
tabBarController.selectedIndex = 0;
// 這只centerView
UIButton *center = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 60, 60)];
[center setImage:[UIImage imageNamed:@"luffy1@2x.png"] forState:UIControlStateNormal];
tabBarController.centerView = center;
}
以上涉及到了WJTabBarButton這個類徙邻,這個類直接繼承自UIView排嫌,在這個類中定義了幾個屬性:
2.2.2創(chuàng)建按鈕
我們這里就可以去創(chuàng)建按鈕了,首先去創(chuàng)建出按鈕缰犁,然后去設(shè)計frame淳地。
自定義的tabBar,我們就可以實現(xiàn)系統(tǒng)實現(xiàn)不了的樣式帅容,比如中間的按鈕樣式特別定制颇象,比如像微博的tabBar中間的‘+’按鈕。我們下面就實現(xiàn)實現(xiàn)下有特殊按鈕的樣式的tabBar并徘。
#pragma mark - 當(dāng)考慮有centerView的時候
- (void)setFrameWithCenter {
// 通用屬性
CGFloat tabBarWidth = self.frame.size.width;
CGFloat tabBarHeight = self.frame.size.height;
NSInteger buttonCount = self.subviews.count;
if (self.centerView) {
// 有centerView的情況
buttonCount -= 1;
}
// centerView的相關(guān)屬性
CGFloat centerWidth = self.centerView.frame.size.width;
CGFloat centerHeight = self.centerView.frame.size.height;
CGFloat centerX = (tabBarWidth - centerWidth) / 2.0f;
CGFloat centerY;
if (centerHeight <= tabBarHeight) {
centerY = (tabBarHeight - centerHeight) / 2.0f;
}
else {
centerY = tabBarHeight - centerHeight;
}
self.centerView.frame = CGRectMake(centerX, centerY, centerWidth, centerHeight);
// 按鈕frame
CGFloat buttonY = 0;
CGFloat buttonHeight = self.frame.size.height;
CGFloat buttonWidth = (tabBarWidth - centerWidth) / buttonCount;
CGFloat buttonX;
int i = 0;
for (UIView *wjview in self.subviews) {
// 拿到按鈕
if (wjview.tag != CenterTag) {
WJTabBarButton *button = (WJTabBarButton *)wjview;
// 計算frame
// 中間按鈕之前的button
if (i < buttonCount / 2) {
buttonX = i * buttonWidth;
}
else {
// 中間按鈕之后的button
buttonX = i * buttonWidth + centerWidth;
}
// 設(shè)置frame
button.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight);
// 設(shè)置默認(rèn)選中的按鈕
if (i == self.selectedIndex) {
button.isSelected = YES;
}
// 設(shè)置tag值
button.tag = ButtonTag + i;
++i;
}
}
}
下面給centerView進(jìn)行賦值
#pragma mark - centerView賦值
-(void)setCenterView:(UIView *)centerView {
_centerView = centerView;
UIView *view = (UIView *)[self viewWithTag:CenterTag];
if (view) {
// 移除之前的tag
[view removeFromSuperview];
}
else {
_centerView.tag = CenterTag;
// 顯示在界面上
[self addSubview:_centerView];
}
}
但在外邊調(diào)用的時候需要傳一個中間按鈕的方法遣钳,把中間按鈕的一些屬性傳給上面的代碼即:
#pragma mark - 外部給centerView賦值
-(void)setCenterView:(UIView *)centerView {
_centerView = centerView;
// 在tabBar上顯示中間的視圖
self.wj_tabBar.centerView = centerView;
}
最后在layoutSubView中實現(xiàn)以上的方法。
2.2.3創(chuàng)建按鈕上的文字和圖片
// 其實就是一個模型
@property (nonatomic, strong) UITabBarItem *item;
// ==================屬性===============
// 按鈕是否選中
@property (nonatomic, assign) BOOL isSelected;
// 按鈕選中顏色
@property (nonatomic, strong) UIColor *selectedColor;
// 按鈕普通狀態(tài)顏色
@property (nonatomic, strong) UIColor *normalColor;
// 添加事件
- (void)addTarget:(id)target action:(SEL)action;
在.m文件中實現(xiàn)相關(guān)方法饮亏;
首先對一些屬性設(shè)置默認(rèn)值耍贾,這里可以提供兩個初始化的方法,以便以后可以用frame的方法進(jìn)行初始化和直接init方法進(jìn)行初始化路幸。
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// 設(shè)置屬性默認(rèn)值
// 選中狀態(tài)的顏色
self.selectedColor = [UIColor redColor];
// 普通狀態(tài)的顏色
self.normalColor = [UIColor lightGrayColor];
}
return self;
}
- (instancetype)init {
if (self = [super init]) {
// 設(shè)置屬性默認(rèn)值
self.selectedColor = [UIColor redColor];
self.normalColor = [UIColor lightGrayColor];
}
return self;
}
然后在按鈕上去創(chuàng)建界面,
去創(chuàng)建子視圖,這里只是單純的創(chuàng)建子視圖,而不去創(chuàng)建子視圖的frame屬性付翁。這里去重寫setter方法;
- (void)setItem:(UITabBarItem *)item {
_item = item;
// 移除按鈕上原來的子視圖
for (UIView *view in self.subviews) {
[view removeFromSuperview];
}
// 創(chuàng)建對應(yīng)的子視圖
// 1.圖片
if (item.image || item.selectedImage) {
_imageView = [[UIImageView alloc]init];
[self addSubview:_imageView];
_imageView.image = item.image;
// 不縮放
[_imageView setContentMode:UIViewContentModeCenter];
}
// 2.文字
if (item.title) {
_textLabel = [[UILabel alloc] init];
[self addSubview:_textLabel];
_textLabel.text = item.title;
_textLabel.textAlignment = NSTextAlignmentCenter;
_textLabel.font = [UIFont systemFontOfSize:12.0];
}
}
下一步就去設(shè)置子視圖的frame屬性,讓其在視圖即將創(chuàng)建的時候去設(shè)置frame屬性简肴,因為在這個時候,tabBar上的按鈕的個數(shù)已經(jīng)全部創(chuàng)建完成百侧,所以使用layoutSubviews
方法設(shè)置frame屬性砰识,這里我們先封裝一個方法去設(shè)置子視圖的frame。在計算視圖的frame的時候最好用相對位置佣渴,最好不要把位置寫死了辫狼,萬一以后要調(diào)整的話,一個參數(shù)更改辛润,其他的參數(shù)位置也會跟著變化膨处。
// 計算frame
- (void)setSubViewFrame {
// 通用
CGFloat buttonW = self.frame.size.width;
CGFloat buttonH = self.frame.size.height;
// 1.計算圖片的frame
// 有圖片
if (_imageView) {
CGFloat imageX = 0;
CGFloat imageY = 0;
CGFloat imageW = buttonW;
CGFloat imageH;
if (_textLabel) {
// 有文字
imageH = buttonH * 4 / 5.0f;
}
else {
// 沒有文字
imageH = buttonH;
}
_imageView.frame = CGRectMake(imageX, imageY, imageW, imageH);
}
// 2.計算文字的frame
if (_textLabel) {
CGFloat textX = 0;
CGFloat textW = buttonW;
CGFloat textY;
CGFloat textH;
if (_imageView) {
// 有圖片
textY = buttonH * 4 / 5.0f;
textH = buttonH / 5.0f;
}
else {
// 沒有圖片
textY = 0;
textH = buttonH;
}
_textLabel.frame = CGRectMake(textX, textY, textW, textH);
}
}
然后把以上的方法在layoutSubviews中去實現(xiàn)下就可以了。
這里可以設(shè)置最終文字所要展示的文字顏色:
// 設(shè)置最終所需屬性
_textLabel.textColor = self.normalColor;
2.3添加按鈕的點擊事件:
#pragma mark - 按鈕的點擊事件
- (void)addTarget:(id)target action:(SEL)action {
_target = target;
_action = action;
}
#pragma mark - 添加事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/*
// 在這里可以先測試下按鈕的點擊效果
if (self.isSelected) {
self.isSelected = NO;
}
else {
self.isSelected = YES;
}
*/
// 響應(yīng)點擊事件
if ([_target respondsToSelector:_action]) {
[_target performSelector:_action withObject:self];
}
}
按鈕被點擊后有選中的狀態(tài)砂竖,之前的按鈕就會失去被選中的狀態(tài)真椿,所以下面就去設(shè)置按鈕的狀態(tài)。
2.3.1按鈕點擊后改變選中狀態(tài)
#pragma mark - 按鈕點擊
- (void)buttonOnClick:(WJTabBarButton *)button {
// 把其他的按鈕變成非選中狀態(tài)
// 獲取到原來的按鈕
WJTabBarButton *wjButton = (WJTabBarButton *)[self viewWithTag:self.selectedIndex + ButtonTag];
wjButton.isSelected = NO;
wjButton.userInteractionEnabled = YES;
// 把當(dāng)前點擊的按鈕變成選中狀態(tài)
button.isSelected = YES;
self.selectedIndex = button.tag - ButtonTag;
button.userInteractionEnabled = NO;
}
2.3.2按鈕改變后更改顏色狀態(tài)和文字
#pragma mark - 改變狀態(tài)
- (void)setIsSelected:(BOOL)isSelected {
_isSelected = isSelected;
if (isSelected) {
// 被選中的時候乎澄,更改選中按鈕的圖片和選中時候顏色
self.imageView.image = self.item.selectedImage;
self.textLabel.textColor = self.selectedColor;
}
else {
// 失去選中的時候突硝,按鈕變成普通狀態(tài),顏色變成普通狀態(tài)的顏色
self.imageView.image = self.item.image;
self.textLabel.textColor = self.normalColor;
}
}
2.4按鈕點擊后的視圖控制器需要切換
分析:點擊按鈕視圖切換置济,實質(zhì)上是WJTabBar想要去切換視圖控制器解恰,但是他自己做不到锋八,需要他人去幫他去做,這就要WJTabBarController去幫他實現(xiàn)視圖的切換护盈。我們可以用代理去實現(xiàn)點擊按鈕實現(xiàn)視圖的切換查库。
那要實現(xiàn)代理,實在什么地方呢黄琼?答案是在選中的下標(biāo)的set方法去實現(xiàn)樊销,所以我們這里需要對選中下標(biāo)的setter方法進(jìn)行重寫。
2.4.1對代理進(jìn)行簡單的分析:
代理的三要素:協(xié)議脏款、代理围苫、委托。這里的協(xié)議就是要切換到指定的視圖控制器撤师;代理就是WJTabBarController剂府;委托就是WJTbaBar。
(1)代理方
首先在代理的.h文件去聲明指定的協(xié)議
// 指定協(xié)議
@protocol WJTabBarDelegate <NSObject>
然后需要一個代理剃盾,這相當(dāng)于是對代理名的重寫
// 需要一個代理
@property (nonatomic, weak) id<WJTabBarDelegate> delegate;
最后在.m文件調(diào)用代理的方法
#pragma mark - 重寫set方法,改變選中下標(biāo),調(diào)用代理方法
- (void)setSelectedIndex:(NSInteger)selectedIndex {
_selectedIndex = selectedIndex;
// 調(diào)用代理的方法
[self.delegate changeControllerWithIndex:self.selectedIndex];
}
(2)委托方
首先要準(zhǔn)守協(xié)議方法
// 遵守協(xié)議方法
@interface WJTabBarController ()<WJTabBarDelegate>
然后去設(shè)置代理人腺占,這個代理人應(yīng)該是在tabBar被加載的時候就應(yīng)該設(shè)置的,所以應(yīng)該在wj_tabBar的懶加載中加上代理人的設(shè)置
#pragma mark - 懶加載
- (WJTabBar *)wj_tabBar {
if (!_wj_tabBar) {
_wj_tabBar = [[WJTabBar alloc]init];
_wj_tabBar.frame = self.tabBar.bounds;
_wj_tabBar.backgroundColor = [UIColor cyanColor];
// 設(shè)置代理人
_wj_tabBar.delegate = self;
}
return _wj_tabBar;
}
最后要實現(xiàn)代理方法,(這是委托人需要執(zhí)行的)
#pragma mark - 實現(xiàn)協(xié)議方法(切換視圖控制器)
- (void)changeControllerWithIndex:(NSInteger)index {
// 切換視圖控制器
self.selectedIndex = index;
}
以上就是自定義按鈕的大概實現(xiàn)步驟痒谴。
三:總結(jié)
要實現(xiàn)自定義的按鈕衰伯,就是需要覆蓋系統(tǒng)原生的按鈕,然后移除tabBar上的所有子控件积蔚,然后在tabBar上去創(chuàng)建一個自定義的tabBar意鲸,然后在tabBar上創(chuàng)建按鈕,在按鈕上去創(chuàng)建圖片和文字尽爆;如果要添加中間的那種特殊定制的按鈕怎顾,只需要把frame值設(shè)置好就應(yīng)該可以了。實現(xiàn)頁面的切換的話漱贱,就需要用到代理槐雾,讓tabBarController去實現(xiàn)tabBar想實現(xiàn)的功能就可以了。
后面還有一篇較為簡單的方法定義tabBar的:
http://www.reibang.com/p/a3002314db32