開發中,經常會遇到各種各樣的奇葩設計要求,因為apple提供的UITabBar樣式單一,只是簡單的"圖片+文字"樣式,高度49又不可以改變。自定義UITabBar成為了唯一的出路。下面我就列舉開發中我經常用到的兩種自定義UITabBar的方式,並且通過比較他們的不同之處,能夠知道何時用何種方式自定義UITabBar。
方式一:
這是真正意義上的自定義UITabBar,因為這種方式需要繼承自UITabBar,但是缺點也很明顯,高度永遠是49,實際開發的項目中的tabBar如果和原生的UITabBar相差不大,可以考慮這種方式。廢話少說,直接上代碼。
#import <UIKit/UIKit.h> @interface WSTabBar : UITabBar @end #import "WSTabBar.h" @implementation WSTabBar - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { // 初始化操作 } return self; } - (void)layoutSubviews { [super layoutSubviews]; // 布局子控件 for (UITabBarItem *item in self.subviews) { // item.title = @"標題"; // item.image = [UIImage imageNamed:@""]; } } @end
// UITabBarController中設置自定義tabBar - (void)setupTabBar { // self.tabBar = [[WSTabBar alloc] init]; // 不能直接賦值,只能通過KVC方式設置tabBar [self setValue:[[WSTabBar alloc] init] forKeyPath:@"tabBar"]; }
以上這種方式實際上還是tabBar,只不過我們可以在tabBar的initWithFrame:方法中給tabBar做一些初始化操作,比如添加一個不同於其他item的button。另外也可以在layoutSubviews中設置每個UITabBarItem和我們自己添加的button的frame。
方式二:
第二種方式是自定義UIView代替tabBar,然后把UIView添加到tabBar控制器上。監聽自定義UIView上每個按鈕的點擊,然后跳轉到指定的tabBar子控制器。這種方式的缺點是,比第一種稍稍復雜了一點點,優點是靈活度高,我們可以天馬行空的把tabBar設計成任何樣式。廢話少說,直接上代碼。
以下是自定義UITabBar:
#import <UIKit/UIKit.h> @class WSTabBar; @protocol WSTabBarDelegate <NSObject> /** 控制器遵守該協議后,用於傳遞tabBar當前選中的按鈕的索引給控制器 */ - (void)tabBar:(WSTabBar *)tabBar withSelectedIndex:(NSInteger)selectedIndex; @end @interface WSTabBar : UIView /** tabBar的item數組 */ @property (nonatomic,strong) NSArray *items; /** tabBar所屬的tabBarController */ @property (nonatomic,strong) UITabBarController *tabBarController; /** block */ @property (nonatomic,strong) void(^selectedIndexBlock)(NSInteger); // 代理 @property (nonatomic,weak) id<WSTabBarDelegate> delegate; @end #import "WSTabBar.h" @interface WSTabBar () /** 當前選中的按鈕 */ @property(nonatomic,strong) UIButton *selectedBtn; @end @implementation WSTabBar //- (instancetype)initWithFrame:(CGRect)frame //{ // if (self = [super initWithFrame:frame]) { // // } // // return self; //} - (void)setItems:(NSArray *)items { _items = items; // 千萬不要漏掉這一句啊!漏掉后,會導致_items為nil NSInteger index = 0; for (UITabBarItem *item in items) { UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; [btn setBackgroundImage:item.image forState:UIControlStateNormal]; [btn setBackgroundImage:item.selectedImage forState:UIControlStateSelected]; btn.tag = index; ++index; [btn addTarget:self action:@selector(btnDidOnClick:) forControlEvents:UIControlEventTouchUpInside]; [self addSubview:btn]; } } - (void)layoutSubviews { [super layoutSubviews]; for (int i = 0; i < self.items.count; i++) { UIButton *btn = self.subviews[i]; CGFloat width = self.frame.size.width / self.items.count; CGFloat height = self.frame.size.height; btn.frame = CGRectMake(i * width, 0, width, height); } } - (void)btnDidOnClick:(UIButton *)btn { // 三部曲 self.selectedBtn.selected = NO; // 上一個按鈕取消選中 btn.selected = YES; // 當前按鈕設置選中 self.selectedBtn = btn; // 當前按鈕賦值給selectedBtn // 5種方法 // 1.tabBarController屬性 /* self.tabBarController.selectedIndex = btn.tag; */ // 2.通知 /* NSMutableDictionary *dict = [NSMutableDictionary dictionary]; dict[@"selectedIndex"] = @(btn.tag); [[NSNotificationCenter defaultCenter] postNotificationName:@"changeSelectedIndexNotification" object:self userInfo:dict]; */ // 3.KVO 控制器或者tabBar監聽selectedBtn.tag的變化 // 4.block /* if (self.selectedIndexBlock) { self.selectedIndexBlock(btn.tag); } */ // 5.代理 if ([self.delegate respondsToSelector:@selector(tabBar:withSelectedIndex:)]) { [self.delegate tabBar:self withSelectedIndex:btn.tag]; } } - (void)dealloc { }
以下是使用UITabBar:
#import <UIKit/UIKit.h> @interface WSTabBarController : UITabBarController @end #import "WSTabBarController.h" #import "WSTabBar.h" #import "OneChildViwController.h" #import "TwoChildViwController.h" #import "ThreeChildViwController.h" #import "FourChildViwController.h" #import "FiveChildViwController.h" @interface WSTabBarController ()<WSTabBarDelegate> @property(nonatomic,strong) NSMutableArray *items; @property(nonatomic,strong) WSTabBar *WSTabBar; @end @implementation WSTabBarController - (NSMutableArray *)items { if (!_items) { _items = [NSMutableArray array]; } return _items; } - (void)viewDidLoad { [super viewDidLoad]; [self setupChildVC]; [self setupTabBar]; // 2.注冊監聽通知 // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeSelectedIndex:) name:@"changeSelectedIndexNotification" object:nil]; // // 3.KVO // [self addObserver:self forKeyPath:@"WSTabBar.selectedBtn.tag" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; } - (void)changeSelectedIndex:(NSNotification *)noti { self.selectedIndex = [noti.userInfo[@"selectedIndex"] integerValue]; } - (void)dealloc { // 移除通知監聽 [[NSNotificationCenter defaultCenter] removeObserver:self]; // 移除KVO監聽 [self removeObserver:self forKeyPath:@"WSTabBar.selectedBtn.tag"]; } - (void)setupChildVC { OneChildViwController *oneVC = [[OneChildViwController alloc] init]; TwoChildViwController *twoVC = [[TwoChildViwController alloc] init]; ThreeChildViwController *threeVC = [[ThreeChildViwController alloc] init]; FourChildViwController *fourVC = [[FourChildViwController alloc] init]; FiveChildViwController *fiveVC = [[FiveChildViwController alloc] init]; [self setupChildVC:oneVC image:@"TabBar_Arena_new" selectedImage:@"TabBar_Arena_selected_new" title:@"競技場"]; [self setupChildVC:twoVC image:@"TabBar_Discovery_new" selectedImage:@"TabBar_Discovery_selected_new" title:@"發現"]; [self setupChildVC:threeVC image:@"TabBar_History_new" selectedImage:@"TabBar_History_selected_new" title:@"開獎信息"]; [self setupChildVC:fourVC image:@"TabBar_LotteryHall_new" selectedImage:@"TabBar_LotteryHall_selected_new" title:@"購彩大廳"]; [self setupChildVC:fiveVC image:@"TabBar_MyLottery_new" selectedImage:@"TabBar_MyLottery_selected_new" title:@"我的彩票"]; } - (void)setupChildVC:(UIViewController *)childVC image:(NSString *)image selectedImage:(NSString *)selectedImage title:(NSString *)title { UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:childVC]; childVC.tabBarItem.image = [UIImage imageNamed:image]; childVC.tabBarItem.selectedImage = [UIImage imageNamed:selectedImage]; childVC.tabBarItem.title = title; [self.items addObject:childVC.tabBarItem]; [self addChildViewController:nav]; } - (void)setupTabBar { WSTabBar *tabBar = [[WSTabBar alloc] init]; tabBar.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height - 49, [UIScreen mainScreen].bounds.size.width, 49); // iphone 4、5、6 tabBar高度為49 // iPhone 4、5、6 UINavigationBar高度為44 // iphone 4、5、6 UINavigationBarBackGround高度為64 所以會有狀態欄被導航欄同化的現象 tabBar.items = self.items; // 不能 tabBar.items = self.tabBar.items; tabBar.backgroundColor = [UIColor orangeColor]; [self.view addSubview:tabBar]; // 1.WSTabBar的tabBarController屬性 // tabBar.tabBarController = self; // 3.KVO(tabBar監聽) // [tabBar addObserver:self forKeyPath:@"selectedBtn.tag" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; // 3.KVO(讓控制器監聽) // self.WSTabBar = tabBar; // [self addObserver:self forKeyPath:@"WSTabBar.selectedBtn.tag" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil]; // 4.block方式 // tabBar.selectedIndexBlock = ^(NSInteger selectedIndex){ // self.selectedIndex = selectedIndex; // }; // 5.代理 tabBar.delegate = self; } // KVO監聽到鍵值變化 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { NSInteger new = [change[NSKeyValueChangeNewKey] integerValue]; self.selectedIndex = new; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; for (UIView *subView in self.tabBar.subviews) { if (![subView isKindOfClass:[WSTabBar class]]) { // 移除原生tabBar上的item [subView removeFromSuperview]; } } } #pragma mark - WSTabBarDelegate - (void)tabBar:(WSTabBar *)tabBar withSelectedIndex:(NSInteger)selectedIndex { self.selectedIndex = selectedIndex; } @end
以上第二種方式,筆者使用了5中方式在自定義的tabBar和tabBarController之間傳值,實現tabBar子控制器的切換。
詳細代碼demo:https://github.com/nlgb/WSTabBar/tree/master/WSTabBar