iOS 自定義TabBarController

一、自定義的思路

iOS中的TabBarController確實已經很強大了,大部分主流iOS應用都會採用。但是往往也不能滿足全部的需求,因此需要自定義TabBar,自定義需要對系統的TabBar工作方式有很好的理解,自定義需要勇氣。

自定義TabBar的原則:儘量利用系統自帶TabBar,只改需要改的地方。


二、自定義TabBar的總體過程
1.先把自帶的TabBar條給取消了
2.自己做一個view,上面放幾個按鈕,設定按鈕的點擊事件.並設置selectIndex。
3.關聯各個子viewController,覆蓋相關事件。

三、細節很重要

1. 讓自己創建的按鈕關聯到viewController:
tabbarselectedIndex屬性.設置這個屬性就行了.
2. 取消系統的高亮:
可以自定義一個按鈕.重寫裏面的setHighhighted方法,什麼也不做就行了.(如果調用super就相當於沒寫)
3. 關於幾個按鈕只選中一個的方法:
設置一個屬性,記錄上一個選中的按鈕.
點擊當前按鈕時,把上一個按鈕設置爲未選中,並把當前按鈕設置爲選中,最後把當前按鈕賦值給上一個按鈕.

四、初步自定義
直接上代碼,詳見註釋。
XNTabBarController.h

[objc] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. #import <UIKit/UIKit.h>  
  2.   
  3. @interface XNTabBarController : UITabBarController  
  4.   
  5. @end  

XNTabBarController.m

[objc] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. //  
  2. //  XNTabBarController.m  
  3. //  
  4. //  
  5. //  Created by neng on 14-6-19.  
  6. //  Copyright (c) 2014年 neng. All rights reserved.  
  7. //  
  8.   
  9. #import "XNTabBarController.h"  
  10. #import "Common.h"  
  11. #import "XNTabBarButton.h"  
  12.   
  13. @interface XNTabBarController ()  
  14. /** 
  15.  *  設置之前選中的按鈕 
  16.  */  
  17. @property (nonatomic, weak) UIButton *selectedBtn;  
  18. @end  
  19.   
  20. @implementation XNTabBarController  
  21.   
  22. - (void)viewDidLoad {  
  23.     [super viewDidLoad];  
  24.     //下面兩個方法在開發中是經常會用到的  
  25. //    NSLog(@"%s",__func__);  
  26. //    NSLog(@"%@",self.view.subviews); //能打印出所有子視圖,和其frame  
  27.     LogFun;  
  28.     LogSubviews(self.view);  
  29.   
  30.   
  31.     //刪除現有的tabBar  
  32.     CGRect rect = self.tabBar.frame;  
  33.     [self.tabBar removeFromSuperview];  //移除TabBarController自帶的下部的條  
  34.   
  35.     //測試添加自己的視圖  
  36.     UIView *myView = [[UIView alloc] init];  
  37.     myView.frame = rect;  
  38.     myView.backgroundColor = [UIColor redColor];  
  39.     [self.view addSubview:myView];  
  40.   
  41.     for (int i = 0; i < 5; i++) {  
  42.         //UIButton *btn = [[UIButton alloc] init];  
  43.         XNTabBarButton *btn = [[XNTabBarButton alloc] init];  
  44.           
  45.         NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];  
  46.         NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];  
  47.   
  48.         [btn setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];  
  49.         [btn setImage:[UIImage imageNamed:imageNameSel] forState:UIControlStateSelected];  
  50.   
  51.         CGFloat x = i * myView.frame.size.width / 5;  
  52.         btn.frame = CGRectMake(x, 0, myView.frame.size.width / 5, myView.frame.size.height);  
  53.   
  54.         [myView addSubview:btn];  
  55.           
  56.         btn.tag = i;//設置按鈕的標記, 方便來索引當前的按鈕,並跳轉到相應的視圖  
  57.   
  58.         //帶參數的監聽方法記得加"冒號"  
  59.         [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];  
  60.   
  61.         //設置剛進入時,第一個按鈕爲選中狀態  
  62.         if (0 == i) {  
  63.             btn.selected = YES;  
  64.             self.selectedBtn = btn;  //設置該按鈕爲選中的按鈕  
  65.         }  
  66.     }  
  67. }  
  68.   
  69. /** 
  70.  *  自定義TabBar的按鈕點擊事件 
  71.  */  
  72. - (void)clickBtn:(UIButton *)button {  
  73.     //1.先將之前選中的按鈕設置爲未選中  
  74.     self.selectedBtn.selected = NO;  
  75.     //2.再將當前按鈕設置爲選中  
  76.     button.selected = YES;  
  77.     //3.最後把當前按鈕賦值爲之前選中的按鈕  
  78.     self.selectedBtn = button;  
  79.       
  80.     //4.跳轉到相應的視圖控制器. (通過selectIndex參數來設置選中了那個控制器)  
  81.     self.selectedIndex = button.tag;  
  82. }  
  83.   
  84. @end  

XNTabBarButton.h

[objc] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. #import <UIKit/UIKit.h>  
  2.   
  3. @interface XNTabBarButton : UIButton  
  4.   
  5. @end  

XNTabBarButton.m

[objc] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. #import "XNTabBarButton.h"  
  2.   
  3. @implementation XNTabBarButton  
  4. /**什麼也不做就可以取消系統按鈕的高亮狀態*/  
  5. - (void)setHighlighted:(BOOL)highlighted{  
  6. //    [super setHighlighted:highlighted];  
  7. }  
  8.   
  9. @end  

五、代碼重構

重構的目的是把代碼放到他最該到的地方去. 提高可讀寫與可拓展性。

對控件的重構要保證可重用性. 做到封裝做其他應用時,可以直接拿過去用的地步.

tips :
1、關於initinitWithFrame:
在對象初始化調用init,會調用initWithFrame方法.
InitinitWithFrame都會被調用.
建議自定義控件不要重init方法,需要初始化重寫initWithFrame方法.
好處:其他人調用無論是調用init,還是調用initWithFrame都會調用initWithFrame方法.

2、關於控件的佈局代碼:
建議寫在layoutSubviews方法中.
不要忘記寫super方法
將設置x,y,frame等寫在這裏面.
3、自定義的Tabbar添加爲系統TabBar的子視圖,這樣TabBar的切換自動隱藏/滑動功能就不用自己做了. (hidebottombaronpush)

重構後的代碼如下
將自定義的TabBar單獨建立,並將代碼移過去。
設置代理方法,工具欄按鈕被選中,記錄從哪裏跳轉到哪裏. 

XNTabBar.h

[objc] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. #import <UIKit/UIKit.h>  
  2. @class XNTabBar;  
  3.   
  4. @protocol XNTabBarDelegate <NSObject>  
  5. /** 
  6.  *  工具欄按鈕被選中, 記錄從哪裏跳轉到哪裏. (方便以後做相應特效) 
  7.  */  
  8. - (void) tabBar:(XNTabBar *)tabBar selectedFrom:(NSInteger) from to:(NSInteger)to;  
  9.   
  10. @end  
  11.   
  12. @interface XNTabBar : UIView  
  13. @property(nonatomic,weak) id<XNTabBarDelegate> delegate;  
  14. /** 
  15.  *  使用特定圖片來創建按鈕, 這樣做的好處就是可擴展性. 拿到別的項目裏面去也能換圖片直接用 
  16.  * 
  17.  *  @param image         普通狀態下的圖片 
  18.  *  @param selectedImage 選中狀態下的圖片 
  19.  */  
  20. -(void)addButtonWithImage:(UIImage *)image selectedImage:(UIImage *) selectedImage;  
  21. @end  


XNTabBar.m


[objc] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. //  
  2. //  XNTabBar.m  
  3. //  
  4. //  Created by neng on 14-6-19.  
  5. //  Copyright (c) 2014年 neng. All rights reserved.  
  6. //  
  7.   
  8. #import "XNTabBar.h"  
  9. #import "XNTabBarButton.h"  
  10.   
  11. @interface XNTabBar ()  
  12. /** 
  13.  *  設置之前選中的按鈕 
  14.  */  
  15. @property (nonatomic, weak) UIButton *selectedBtn;  
  16. @end  
  17.   
  18. @implementation XNTabBar  
  19.   
  20. /** 
  21.  *  在這個方法裏寫控件初始化的東西, 調用init方法時會調用 
  22.  */  
  23. //- (id)initWithFrame:(CGRect)frame {  
  24. //  if (self = [super initWithFrame:frame]) {  
  25. //      //添加按鈕  
  26. //      for (int i = 0; i < 5; i++) { //取消掉特定的數字  
  27. //          //UIButton *btn = [[UIButton alloc] init];  
  28. //          XNTabBarButton *btn = [[XNTabBarButton alloc] init];  
  29. //  
  30. //          NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];  
  31. //          NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];  
  32. //  
  33. //          [btn setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];  
  34. //          [btn setImage:[UIImage imageNamed:imageNameSel] forState:UIControlStateSelected];  
  35. //  
  36. //          [self addSubview:btn];  
  37. //  
  38. //          btn.tag = i; //設置按鈕的標記, 方便來索引當前的按鈕,並跳轉到相應的視圖  
  39. //  
  40. //          //帶參數的監聽方法記得加"冒號"  
  41. //          [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];  
  42. //  
  43. //          if (0 == i) {  
  44. //              [self clickBtn:btn];  
  45. //          }  
  46. //      }  
  47. //  }  
  48. //  return self;  
  49. //}  
  50.   
  51. - (void)addButtonWithImage:(UIImage *)image selectedImage:(UIImage *)selectedImage {  
  52.     UIButton *btn = [[UIButton alloc] init];  
  53.   
  54.     [btn setImage:image forState:UIControlStateNormal];  
  55.     [btn setImage:selectedImage forState:UIControlStateSelected];  
  56.   
  57.     [self addSubview:btn];  
  58.   
  59.     //帶參數的監聽方法記得加"冒號"  
  60.     [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];  
  61.   
  62.     //如果是第一個按鈕, 則選中(按順序一個個添加)  
  63.     if (self.subviews.count == 1) {  
  64.         [self clickBtn:btn];  
  65.     }  
  66. }  
  67.   
  68. /**專門用來佈局子視圖, 別忘了調用super方法*/  
  69. - (void)layoutSubviews {  
  70.     [super layoutSubviews];  
  71.   
  72.     int count = self.subviews.count;  
  73.     for (int i = 0; i < count; i++) {  
  74.         //取得按鈕  
  75.         UIButton *btn = self.subviews[i];  
  76.   
  77.         btn.tag = i; //設置按鈕的標記, 方便來索引當前的按鈕,並跳轉到相應的視圖  
  78.   
  79.         CGFloat x = i * self.bounds.size.width / count;  
  80.         CGFloat y = 0;  
  81.         CGFloat width = self.bounds.size.width / count;  
  82.         CGFloat height = self.bounds.size.height;  
  83.         btn.frame = CGRectMake(x, y, width, height);  
  84.     }  
  85. }  
  86.   
  87. /** 
  88.  *  自定義TabBar的按鈕點擊事件 
  89.  */  
  90. - (void)clickBtn:(UIButton *)button {  
  91.     //1.先將之前選中的按鈕設置爲未選中  
  92.     self.selectedBtn.selected = NO;  
  93.     //2.再將當前按鈕設置爲選中  
  94.     button.selected = YES;  
  95.     //3.最後把當前按鈕賦值爲之前選中的按鈕  
  96.     self.selectedBtn = button;  
  97.   
  98.     //卻換視圖控制器的事情,應該交給controller來做  
  99.     //最好這樣寫, 先判斷該代理方法是否實現  
  100.     if ([self.delegate respondsToSelector:@selector(tabBar:selectedFrom:to:)]) {  
  101.         [self.delegate tabBar:self selectedFrom:self.selectedBtn.tag to:button.tag];  
  102.     }  
  103.   
  104.     //4.跳轉到相應的視圖控制器. (通過selectIndex參數來設置選中了那個控制器)  
  105.     //self.selectedIndex = button.tag;  
  106. }  
  107.   
  108. @end  

原先的XNTabBarController.m經過修改後,註釋了原先的代碼。

[objc] view plaincopy在CODE上查看代碼片派生到我的代碼片
  1. //  
  2. //  XNTabBarController.m  
  3. //  
  4. //  Created by neng on 14-6-19.  
  5. //  Copyright (c) 2014年 neng. All rights reserved.  
  6. //  
  7.   
  8. #import "XNTabBarController.h"  
  9. #import "XNTabBarButton.h"  
  10. #import "XNTabBar.h"  
  11.   
  12. @interface XNTabBarController () <XNTabBarDelegate>  
  13. /** 
  14.  *  設置之前選中的按鈕 
  15.  */  
  16. @property (nonatomic, weak) UIButton *selectedBtn;  
  17. @end  
  18.   
  19. @implementation XNTabBarController  
  20.   
  21. - (void)viewDidLoad {  
  22.     [super viewDidLoad];  
  23.     //下面兩個方法在開發中是經常會用到的  
  24. //    NSLog(@"%s",__func__);  
  25. //    NSLog(@"%@",self.view.subviews); //能打印出所有子視圖,和其frame  
  26. //  LogFun;  
  27. //  LogSubviews(self.view);  
  28.     //Hell  
  29.   
  30.   
  31.     //刪除現有的tabBar  
  32.     CGRect rect = self.tabBar.bounds//這裏要用bounds來加, 否則會加到下面去.看不見  
  33.     LogFrame(self.tabBar);  
  34.     //[self.tabBar removeFromSuperview];  //移除TabBarController自帶的下部的條  
  35.   
  36.     //測試添加自己的視圖  
  37.     XNTabBar *myView = [[XNTabBar alloc] init]; //設置代理必須改掉前面的類型,不能用UIView  
  38.     myView.delegate = self//設置代理  
  39.     myView.frame = rect;  
  40.     [self.tabBar addSubview:myView]; //添加到系統自帶的tabBar上, 這樣可以用的的事件方法. 而不必自己去寫  
  41.       
  42.     //爲控制器添加按鈕  
  43.     for (int i=0; i<self.viewControllers.count; i++) { //根據有多少個子視圖控制器來進行添加按鈕  
  44.           
  45.         NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];  
  46.         NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];  
  47.           
  48.         UIImage *image = [UIImage imageNamed:imageName];  
  49.         UIImage *imageSel = [UIImage imageNamed:imageNameSel];  
  50.           
  51.         [myView addButtonWithImage:image selectedImage:imageSel];  
  52.     }  
  53.       
  54.   
  55. //    //添加按鈕  
  56. //  for (int i = 0; i < 5; i++) {  
  57. //      //UIButton *btn = [[UIButton alloc] init];  
  58. //        XNTabBarButton *btn = [[XNTabBarButton alloc] init];  
  59. //  
  60. //      NSString *imageName = [NSString stringWithFormat:@"TabBar%d", i + 1];  
  61. //      NSString *imageNameSel = [NSString stringWithFormat:@"TabBar%dSel", i + 1];  
  62. //  
  63. //      [btn setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];  
  64. //      [btn setImage:[UIImage imageNamed:imageNameSel] forState:UIControlStateSelected];  
  65. //  
  66. //      CGFloat x = i * myView.frame.size.width / 5;  
  67. //      btn.frame = CGRectMake(x, 0, myView.frame.size.width / 5, myView.frame.size.height);  
  68. //  
  69. //      [myView addSubview:btn];  
  70. //  
  71. //        btn.tag = i;//設置按鈕的標記, 方便來索引當前的按鈕,並跳轉到相應的視圖  
  72. //  
  73. //      //帶參數的監聽方法記得加"冒號"  
  74. //      [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];  
  75. //  
  76. //      //設置剛進入時,第一個按鈕爲選中狀態  
  77. //      if (0 == i) {  
  78. //          btn.selected = YES;  
  79. //          self.selectedBtn = btn;  //設置該按鈕爲選中的按鈕  
  80. //      }  
  81. //  }  
  82. }  
  83.   
  84. /**永遠別忘記設置代理*/  
  85. - (void)tabBar:(XNTabBar *)tabBar selectedFrom:(NSInteger)from to:(NSInteger)to {  
  86.     self.selectedIndex = to;  
  87. }  
  88.   
  89. /** 
  90.  *  自定義TabBar的按鈕點擊事件 
  91.  */  
  92. //- (void)clickBtn:(UIButton *)button {  
  93. //  //1.先將之前選中的按鈕設置爲未選中  
  94. //  self.selectedBtn.selected = NO;  
  95. //  //2.再將當前按鈕設置爲選中  
  96. //  button.selected = YES;  
  97. //  //3.最後把當前按鈕賦值爲之前選中的按鈕  
  98. //  self.selectedBtn = button;  
  99. //  
  100. //    //4.跳轉到相應的視圖控制器. (通過selectIndex參數來設置選中了那個控制器)  
  101. //    self.selectedIndex = button.tag;  
  102. //}  
  103.   
  104. @end  

自定義後的效果圖:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章