自定义UITabBar的两种方式
开发中,经常会遇到各种各样的奇葩设计要求,因为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