在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 <NSObject>
然后需要一个代理,这相当于是对代理名的重写
// 需要一个代理
@property (nonatomic, weak) id<WJTabBarDelegate> delegate;
最后在.m文件调用代理的方法
#pragma mark - 重写set方法,改变选中下标,调用代理方法
- (void)setSelectedIndex:(NSInteger)selectedIndex {
_selectedIndex = selectedIndex;
// 调用代理的方法
[self.delegate changeControllerWithIndex:self.selectedIndex];
}
(2)委托方
首先要准守协议方法
// 遵守协议方法
@interface WJTabBarController ()<WJTabBarDelegate>
然后去设置代理人,这个代理人应该是在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的:
http://www.jianshu.com/p/a3002314db32