一、自定義的思路
iOS中的TabBarController確實已經很強大了,大部分主流iOS應用都會採用。但是往往也不能滿足全部的需求,因此需要自定義TabBar,自定義需要對系統的TabBar工作方式有很好的理解,自定義需要勇氣。
自定義TabBar的原則:儘量利用系統自帶TabBar,只改需要改的地方。
二、自定義TabBar的總體過程
1.先把自帶的TabBar條給取消了
2.自己做一個view,上面放幾個按鈕,設定按鈕的點擊事件.並設置selectIndex。
3.關聯各個子viewController,覆蓋相關事件。
三、細節很重要
1. 讓自己創建的按鈕關聯到viewController:
•用tabbar的selectedIndex屬性.設置這個屬性就行了.
2. 取消系統的高亮:
•可以自定義一個按鈕.重寫裏面的setHighhighted方法,什麼也不做就行了.(如果調用super就相當於沒寫)
3. 關於幾個按鈕只選中一個的方法:
•設置一個屬性,記錄上一個選中的按鈕.
•點擊當前按鈕時,把上一個按鈕設置爲未選中,並把當前按鈕設置爲選中,最後把當前按鈕賦值給上一個按鈕.
四、初步自定義
直接上代碼,詳見註釋。
- #import <UIKit/UIKit.h>
- @interface XNTabBarController : UITabBarController
- @end
XNTabBarController.m
- //
- // XNTabBarController.m
- //
- //
- // Created by neng on 14-6-19.
- // Copyright (c) 2014年 neng. All rights reserved.
- //
- #import "XNTabBarController.h"
- #import "Common.h"
- #import "XNTabBarButton.h"
- @interface XNTabBarController ()
- /**
- * 設置之前選中的按鈕
- */
- @property (nonatomic, weak) UIButton *selectedBtn;
- @end
- @implementation XNTabBarController
- - (void)viewDidLoad {
- [super viewDidLoad];
- //下面兩個方法在開發中是經常會用到的
- // NSLog(@"%s",__func__);
- // NSLog(@"%@",self.view.subviews); //能打印出所有子視圖,和其frame
- LogFun;
- LogSubviews(self.view);
- //刪除現有的tabBar
- CGRect rect = self.tabBar.frame;
- [self.tabBar removeFromSuperview]; //移除TabBarController自帶的下部的條
- //測試添加自己的視圖
- UIView *myView = [[UIView alloc] init];
- myView.frame = rect;
- myView.backgroundColor = [UIColor redColor];
- [self.view addSubview:myView];
- for (int i = 0; i < 5; i++) {
- //UIButton *btn = [[UIButton alloc] init];
- XNTabBarButton *btn = [[XNTabBarButton alloc] init];
- NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];
- NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];
- [btn setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
- [btn setImage:[UIImage imageNamed:imageNameSel] forState:UIControlStateSelected];
- CGFloat x = i * myView.frame.size.width / 5;
- btn.frame = CGRectMake(x, 0, myView.frame.size.width / 5, myView.frame.size.height);
- [myView addSubview:btn];
- btn.tag = i;//設置按鈕的標記, 方便來索引當前的按鈕,並跳轉到相應的視圖
- //帶參數的監聽方法記得加"冒號"
- [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
- //設置剛進入時,第一個按鈕爲選中狀態
- if (0 == i) {
- btn.selected = YES;
- self.selectedBtn = btn; //設置該按鈕爲選中的按鈕
- }
- }
- }
- /**
- * 自定義TabBar的按鈕點擊事件
- */
- - (void)clickBtn:(UIButton *)button {
- //1.先將之前選中的按鈕設置爲未選中
- self.selectedBtn.selected = NO;
- //2.再將當前按鈕設置爲選中
- button.selected = YES;
- //3.最後把當前按鈕賦值爲之前選中的按鈕
- self.selectedBtn = button;
- //4.跳轉到相應的視圖控制器. (通過selectIndex參數來設置選中了那個控制器)
- self.selectedIndex = button.tag;
- }
- @end
XNTabBarButton.h
- #import <UIKit/UIKit.h>
- @interface XNTabBarButton : UIButton
- @end
XNTabBarButton.m
- #import "XNTabBarButton.h"
- @implementation XNTabBarButton
- /**什麼也不做就可以取消系統按鈕的高亮狀態*/
- - (void)setHighlighted:(BOOL)highlighted{
- // [super setHighlighted:highlighted];
- }
- @end
五、代碼重構
重構的目的是把代碼放到他最該到的地方去. 提高可讀寫與可拓展性。
對控件的重構要保證可重用性. 做到封裝做其他應用時,可以直接拿過去用的地步.
tips :
1、關於init與initWithFrame:
•在對象初始化調用init時,會調用initWithFrame方法.
•Init與initWithFrame都會被調用.
•建議自定義控件不要重寫init方法,需要初始化時重寫initWithFrame方法.
•好處:其他人調用無論是調用init,還是調用initWithFrame都會調用initWithFrame方法.
2、關於控件的佈局代碼:
•建議寫在layoutSubviews方法中.
•不要忘記寫super方法
•將設置x,y,frame等寫在這裏面.
3、將自定義的Tabbar添加爲系統TabBar的子視圖,這樣TabBar的切換自動隱藏/滑動功能就不用自己做了.
(hidebottombaronpush)
重構後的代碼如下:
將自定義的TabBar單獨建立,並將代碼移過去。
設置代理方法,工具欄按鈕被選中,記錄從哪裏跳轉到哪裏.
- #import <UIKit/UIKit.h>
- @class XNTabBar;
- @protocol XNTabBarDelegate <NSObject>
- /**
- * 工具欄按鈕被選中, 記錄從哪裏跳轉到哪裏. (方便以後做相應特效)
- */
- - (void) tabBar:(XNTabBar *)tabBar selectedFrom:(NSInteger) from to:(NSInteger)to;
- @end
- @interface XNTabBar : UIView
- @property(nonatomic,weak) id<XNTabBarDelegate> delegate;
- /**
- * 使用特定圖片來創建按鈕, 這樣做的好處就是可擴展性. 拿到別的項目裏面去也能換圖片直接用
- *
- * @param image 普通狀態下的圖片
- * @param selectedImage 選中狀態下的圖片
- */
- -(void)addButtonWithImage:(UIImage *)image selectedImage:(UIImage *) selectedImage;
- @end
- //
- // XNTabBar.m
- //
- // Created by neng on 14-6-19.
- // Copyright (c) 2014年 neng. All rights reserved.
- //
- #import "XNTabBar.h"
- #import "XNTabBarButton.h"
- @interface XNTabBar ()
- /**
- * 設置之前選中的按鈕
- */
- @property (nonatomic, weak) UIButton *selectedBtn;
- @end
- @implementation XNTabBar
- /**
- * 在這個方法裏寫控件初始化的東西, 調用init方法時會調用
- */
- //- (id)initWithFrame:(CGRect)frame {
- // if (self = [super initWithFrame:frame]) {
- // //添加按鈕
- // for (int i = 0; i < 5; i++) { //取消掉特定的數字
- // //UIButton *btn = [[UIButton alloc] init];
- // XNTabBarButton *btn = [[XNTabBarButton alloc] init];
- //
- // NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];
- // NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];
- //
- // [btn setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
- // [btn setImage:[UIImage imageNamed:imageNameSel] forState:UIControlStateSelected];
- //
- // [self addSubview:btn];
- //
- // btn.tag = i; //設置按鈕的標記, 方便來索引當前的按鈕,並跳轉到相應的視圖
- //
- // //帶參數的監聽方法記得加"冒號"
- // [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
- //
- // if (0 == i) {
- // [self clickBtn:btn];
- // }
- // }
- // }
- // return self;
- //}
- - (void)addButtonWithImage:(UIImage *)image selectedImage:(UIImage *)selectedImage {
- UIButton *btn = [[UIButton alloc] init];
- [btn setImage:image forState:UIControlStateNormal];
- [btn setImage:selectedImage forState:UIControlStateSelected];
- [self addSubview:btn];
- //帶參數的監聽方法記得加"冒號"
- [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
- //如果是第一個按鈕, 則選中(按順序一個個添加)
- if (self.subviews.count == 1) {
- [self clickBtn:btn];
- }
- }
- /**專門用來佈局子視圖, 別忘了調用super方法*/
- - (void)layoutSubviews {
- [super layoutSubviews];
- int count = self.subviews.count;
- for (int i = 0; i < count; i++) {
- //取得按鈕
- UIButton *btn = self.subviews[i];
- btn.tag = i; //設置按鈕的標記, 方便來索引當前的按鈕,並跳轉到相應的視圖
- CGFloat x = i * self.bounds.size.width / count;
- CGFloat y = 0;
- CGFloat width = self.bounds.size.width / count;
- CGFloat height = self.bounds.size.height;
- btn.frame = CGRectMake(x, y, width, height);
- }
- }
- /**
- * 自定義TabBar的按鈕點擊事件
- */
- - (void)clickBtn:(UIButton *)button {
- //1.先將之前選中的按鈕設置爲未選中
- self.selectedBtn.selected = NO;
- //2.再將當前按鈕設置爲選中
- button.selected = YES;
- //3.最後把當前按鈕賦值爲之前選中的按鈕
- self.selectedBtn = button;
- //卻換視圖控制器的事情,應該交給controller來做
- //最好這樣寫, 先判斷該代理方法是否實現
- if ([self.delegate respondsToSelector:@selector(tabBar:selectedFrom:to:)]) {
- [self.delegate tabBar:self selectedFrom:self.selectedBtn.tag to:button.tag];
- }
- //4.跳轉到相應的視圖控制器. (通過selectIndex參數來設置選中了那個控制器)
- //self.selectedIndex = button.tag;
- }
- @end
原先的XNTabBarController.m經過修改後,註釋了原先的代碼。
- //
- // XNTabBarController.m
- //
- // Created by neng on 14-6-19.
- // Copyright (c) 2014年 neng. All rights reserved.
- //
- #import "XNTabBarController.h"
- #import "XNTabBarButton.h"
- #import "XNTabBar.h"
- @interface XNTabBarController () <XNTabBarDelegate>
- /**
- * 設置之前選中的按鈕
- */
- @property (nonatomic, weak) UIButton *selectedBtn;
- @end
- @implementation XNTabBarController
- - (void)viewDidLoad {
- [super viewDidLoad];
- //下面兩個方法在開發中是經常會用到的
- // NSLog(@"%s",__func__);
- // NSLog(@"%@",self.view.subviews); //能打印出所有子視圖,和其frame
- // LogFun;
- // LogSubviews(self.view);
- //Hell
- //刪除現有的tabBar
- CGRect rect = self.tabBar.bounds; //這裏要用bounds來加, 否則會加到下面去.看不見
- LogFrame(self.tabBar);
- //[self.tabBar removeFromSuperview]; //移除TabBarController自帶的下部的條
- //測試添加自己的視圖
- XNTabBar *myView = [[XNTabBar alloc] init]; //設置代理必須改掉前面的類型,不能用UIView
- myView.delegate = self; //設置代理
- myView.frame = rect;
- [self.tabBar addSubview:myView]; //添加到系統自帶的tabBar上, 這樣可以用的的事件方法. 而不必自己去寫
- //爲控制器添加按鈕
- for (int i=0; i<self.viewControllers.count; i++) { //根據有多少個子視圖控制器來進行添加按鈕
- NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];
- NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];
- UIImage *image = [UIImage imageNamed:imageName];
- UIImage *imageSel = [UIImage imageNamed:imageNameSel];
- [myView addButtonWithImage:image selectedImage:imageSel];
- }
- // //添加按鈕
- // for (int i = 0; i < 5; i++) {
- // //UIButton *btn = [[UIButton alloc] init];
- // XNTabBarButton *btn = [[XNTabBarButton alloc] init];
- //
- // NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];
- // NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];
- //
- // [btn setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
- // [btn setImage:[UIImage imageNamed:imageNameSel] forState:UIControlStateSelected];
- //
- // CGFloat x = i * myView.frame.size.width / 5;
- // btn.frame = CGRectMake(x, 0, myView.frame.size.width / 5, myView.frame.size.height);
- //
- // [myView addSubview:btn];
- //
- // btn.tag = i;//設置按鈕的標記, 方便來索引當前的按鈕,並跳轉到相應的視圖
- //
- // //帶參數的監聽方法記得加"冒號"
- // [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
- //
- // //設置剛進入時,第一個按鈕爲選中狀態
- // if (0 == i) {
- // btn.selected = YES;
- // self.selectedBtn = btn; //設置該按鈕爲選中的按鈕
- // }
- // }
- }
- /**永遠別忘記設置代理*/
- - (void)tabBar:(XNTabBar *)tabBar selectedFrom:(NSInteger)from to:(NSInteger)to {
- self.selectedIndex = to;
- }
- /**
- * 自定義TabBar的按鈕點擊事件
- */
- //- (void)clickBtn:(UIButton *)button {
- // //1.先將之前選中的按鈕設置爲未選中
- // self.selectedBtn.selected = NO;
- // //2.再將當前按鈕設置爲選中
- // button.selected = YES;
- // //3.最後把當前按鈕賦值爲之前選中的按鈕
- // self.selectedBtn = button;
- //
- // //4.跳轉到相應的視圖控制器. (通過selectIndex參數來設置選中了那個控制器)
- // self.selectedIndex = button.tag;
- //}
- @end
自定義後的效果圖: