在iOS原生的tabBar中,能够实现按钮的点击事件,能够实现视图控制器的切换等,但是在实际工程中,对于tabBar的要求的功能往往是系统自己实现不了的,所以我们这里就需要用到自定义的tabBar了。 对于tabBar上展示视图控制器,我们会采用的是在把几个视图控制直接加载到tabBarController上去。这里新建三个视图控制器,由于在 一、系统样式 ViewController会有其他代码,所以我们这里另一写一个类,在这里只设置一个背景颜色就可以了。所以我们先新建一个类叫做WJViewController,让它继承自UIViewController。这里设置视图的背景颜色,这里可以设置为随机色。
// 设置背景颜色为随机色 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。然后我们去实现相关方法。 1.首先创建一个tabBarController,用于接收实例化好的视图控制器。 // 1.创建标签栏控制器 UITabBarController *tabBarController = [[UITabBarController alloc]init];
2.然后就可创建需要让标签栏控制器管理的子视图控制器:
// 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.对子视图控制器进行管理 有两种方案可以选择:一是用数组进行接收,二是用一个方法进行接收子视图控制器。 法一: //tabBarController.viewControllers = @[first, second, third];
法二:
[tabBarController addChildViewController:first]; [tabBarController addChildViewController:second]; [tabBarController addChildViewController:third];复制代码
这里最好使用第二种方法进行对子视图控制器进行管理。 代码进行到这里就可以把视图控制器tabBarController展示在Windows上了,所以需要把tabBarController设为根视图。
// 可以通过每个已经显示在界面上的视图,拿到当前应用程序的window self.view.window.rootViewController = tabBarController;}复制代码
这样代码进行到这里系统样式的功能就已经实现了,但是对于我们实际要求的功能相差甚远,所以我们开始自定义的tabBar的创建。 二、自定义样式 下面我们来简单分析下系统的tabBar的功能。 2.1分析 分析首先按钮有按钮点击事件,点击按钮后会改变相应的视图控制器,而且点击按钮后的图标会有相应的改变。而自定义的tabBar也需要达到这些功能,甚至还需要达到自定义按钮的形状的和功能的要求。 对于系统的tabBar来说,能够展示按钮的文字和图片,而这些内容是加载在tabBar的_UITabBarBackgroundView上的UITabBarButton上的UILabel和UIImageView上的。而我们要自定义按钮就需要覆盖系统本身的控件,但这些控件我们是拿不到的,用点语法这些控件是不能联想出来的,但我们又要实现和系统一样的功能,我们只能用一个方法把tabBar上的所有控件移除,因此要用遍历的方法实现移除;我们移除了系统自带的tabBar后就需要自定义一个wjTabBar来代替原有的tabBar。然后在wjTabBar上添加按钮,然后设置相应的点击事件,更改相应的状态就可实现和系统一样的功能。
// 声明一个wjTabBar的全局变量@property (nonatomic, strong) WJTabBar *wj_tabBar;复制代码
- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // 2.删除自动创建的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设置懒加载
#pragma mark - 懒加载- (WJTabBar *)wj_tabBar { if (!_wj_tabBar) { // 创建wj_tabBar _wj_tabBar = [[WJTabBar alloc]init]; // 设置frame _wj_tabBar.frame = self.tabBar.bounds; // 设置一个背景颜色 _wj_tabBar.backgroundColor = [UIColor cyanColor]; } return _wj_tabBar;}复制代码
2.2现在分析下按钮的创建: 按钮的创建应该视图控制器创建有关,所以在设计方法的时候应该传一个视图控制器的参数;按钮还应该设置文字,选中时的图片和普通状态下的图片。 2.2.1把tabBar展示到视图上 考虑到后期调用的方便,可以在.h文件中暴露出接口,以供使用;所以在.h文件中设置方法,然后在.m文件实现相关的方法。
#pragma mark - 添加子视图控制器- (void)addController:(UIViewController *)controller withTitle:(NSString *)title imageName:(NSString *)imageName selectedImageName:(NSString *)selectedImageName { // 设置tabBarItem的子视图 controller.tabBarItem.title = title; controller.tabBarItem.image = [[UIImage imageNamed:imageName] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; controller.tabBarItem.selectedImage = [[UIImage imageNamed:selectedImageName] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; // 将视图控制器添加到标签栏控制器中 [self addChildViewController:controller]; // 可以让自己定制的tabBar去创建一个对应的按钮 [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 - 创建视图控制器- (void)wj_creatControll { // 1.创建标签栏控制器 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"]; // 可以通过每个已经显示在界面上的视图,拿到当前应用程序的window self.view.window.rootViewController = tabBarController; // 设置第几个被选中 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创建按钮 我们这里就可以去创建按钮了,首先去创建出按钮,然后去设计frame。 自定义的tabBar,我们就可以实现系统实现不了的样式,比如中间的按钮样式特别定制,比如像微博的tabBar中间的‘+’按钮。我们下面就实现实现下有特殊按钮的样式的tabBar。
#pragma mark - 当考虑有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的相关属性 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; } // 设置frame button.frame = CGRectMake(buttonX, buttonY, buttonWidth, buttonHeight); // 设置默认选中的按钮 if (i == self.selectedIndex) { button.isSelected = YES; } // 设置tag值 button.tag = ButtonTag + i; ++i; } }}复制代码
下面给centerView进行赋值
#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]; }}复制代码
但在外边调用的时候需要传一个中间按钮的方法,把中间按钮的一些属性传给上面的代码即:
#pragma mark - 外部给centerView赋值-(void)setCenterView:(UIView *)centerView { _centerView = centerView; // 在tabBar上显示中间的视图 self.wj_tabBar.centerView = centerView;}复制代码
最后在layoutSubView中实现以上的方法。
2.2.3创建按钮上的文字和图片
// 其实就是一个模型@property (nonatomic, strong) UITabBarItem *item;// ==================属性===============// 按钮是否选中@property (nonatomic, assign) BOOL isSelected;// 按钮选中颜色@property (nonatomic, strong) UIColor *selectedColor;// 按钮普通状态颜色@property (nonatomic, strong) UIColor *normalColor;// 添加事件- (void)addTarget:(id)target action:(SEL)action;复制代码
在.m文件中实现相关方法;
首先对一些属性设置默认值,这里可以提供两个初始化的方法,以便以后可以用frame的方法进行初始化和直接init方法进行初始化。
- (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { // 设置属性默认值 // 选中状态的颜色 self.selectedColor = [UIColor redColor]; // 普通状态的颜色 self.normalColor = [UIColor lightGrayColor]; } return self;}复制代码
- (instancetype)init { if (self = [super init]) { // 设置属性默认值 self.selectedColor = [UIColor redColor]; self.normalColor = [UIColor lightGrayColor]; } return self;}复制代码
然后在按钮上去创建界面, 去创建子视图,这里只是单纯的创建子视图,而不去创建子视图的frame属性。这里去重写setter方法;
- (void)setItem:(UITabBarItem *)item { _item = item; // 移除按钮上原来的子视图 for (UIView *view in self.subviews) { [view removeFromSuperview]; } // 创建对应的子视图 // 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]; }}复制代码
下一步就去设置子视图的frame属性,让其在视图即将创建的时候去设置frame属性,因为在这个时候,tabBar上的按钮的个数已经全部创建完成,所以使用layoutSubviews
方法设置frame属性,这里我们先封装一个方法去设置子视图的frame。在计算视图的frame的时候最好用相对位置,最好不要把位置写死了,万一以后要调整的话,一个参数更改,其他的参数位置也会跟着变化。
// 计算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中去实现下就可以了。 这里可以设置最终文字所要展示的文字颜色:
// 设置最终所需属性 _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; } */ // 响应点击事件 if ([_target respondsToSelector:_action]) { [_target performSelector:_action withObject:self]; }}复制代码
按钮被点击后有选中的状态,之前的按钮就会失去被选中的状态,所以下面就去设置按钮的状态。 2.3.1按钮点击后改变选中状态
#pragma mark - 按钮点击- (void)buttonOnClick:(WJTabBarButton *)button { // 把其他的按钮变成非选中状态 // 获取到原来的按钮 WJTabBarButton *wjButton = (WJTabBarButton *)[self viewWithTag:self.selectedIndex + ButtonTag]; wjButton.isSelected = NO; wjButton.userInteractionEnabled = YES; // 把当前点击的按钮变成选中状态 button.isSelected = YES; self.selectedIndex = button.tag - ButtonTag; button.userInteractionEnabled = NO;}复制代码
2.3.2按钮改变后更改颜色状态和文字
#pragma mark - 改变状态- (void)setIsSelected:(BOOL)isSelected { _isSelected = isSelected; if (isSelected) { // 被选中的时候,更改选中按钮的图片和选中时候颜色 self.imageView.image = self.item.selectedImage; self.textLabel.textColor = self.selectedColor; } else { // 失去选中的时候,按钮变成普通状态,颜色变成普通状态的颜色 self.imageView.image = self.item.image; self.textLabel.textColor = self.normalColor; }}复制代码
2.4按钮点击后的视图控制器需要切换 分析:点击按钮视图切换,实质上是WJTabBar想要去切换视图控制器,但是他自己做不到,需要他人去帮他去做,这就要WJTabBarController去帮他实现视图的切换。我们可以用代理去实现点击按钮实现视图的切换。 那要实现代理,实在什么地方呢?答案是在选中的下标的set方法去实现,所以我们这里需要对选中下标的setter方法进行重写。
2.4.1对代理进行简单的分析: 代理的三要素:协议、代理、委托。这里的协议就是要切换到指定的视图控制器;代理就是WJTabBarController;委托就是WJTbaBar。 (1)代理方 首先在代理的.h文件去声明指定的协议
// 指定协议@protocol WJTabBarDelegate复制代码
然后需要一个代理,这相当于是对代理名的重写
// 需要一个代理@property (nonatomic, weak) iddelegate;复制代码
最后在.m文件调用代理的方法
#pragma mark - 重写set方法,改变选中下标,调用代理方法- (void)setSelectedIndex:(NSInteger)selectedIndex { _selectedIndex = selectedIndex; // 调用代理的方法 [self.delegate changeControllerWithIndex:self.selectedIndex];}复制代码
(2)委托方 首先要准守协议方法
// 遵守协议方法@interface WJTabBarController ()复制代码
然后去设置代理人,这个代理人应该是在tabBar被加载的时候就应该设置的,所以应该在wj_tabBar的懒加载中加上代理人的设置
#pragma mark - 懒加载- (WJTabBar *)wj_tabBar { if (!_wj_tabBar) { _wj_tabBar = [[WJTabBar alloc]init]; _wj_tabBar.frame = self.tabBar.bounds; _wj_tabBar.backgroundColor = [UIColor cyanColor]; // 设置代理人 _wj_tabBar.delegate = self; } return _wj_tabBar;}复制代码
最后要实现代理方法,(这是委托人需要执行的)
#pragma mark - 实现协议方法(切换视图控制器)- (void)changeControllerWithIndex:(NSInteger)index { // 切换视图控制器 self.selectedIndex = index;}复制代码
以上就是自定义按钮的大概实现步骤。
三:总结 要实现自定义的按钮,就是需要覆盖系统原生的按钮,然后移除tabBar上的所有子控件,然后在tabBar上去创建一个自定义的tabBar,然后在tabBar上创建按钮,在按钮上去创建图片和文字;如果要添加中间的那种特殊定制的按钮,只需要把frame值设置好就应该可以了。实现页面的切换的话,就需要用到代理,让tabBarController去实现tabBar想实现的功能就可以了。
后面还有一篇较为简单的方法定义tabBar的: https://juejin.im/post/5a3143995188253da72e735f