在开发过程中,TabBarController是几乎每个APP都需要用到的,其内部默认包含一个UITabBar,但这个TabBar并不是很好用,所以我们需要自定义TabBar及上面的Button,以实现我们自己想要的效果。
接下来我们开始自定义。
首先是TabBar的.h文件:
#import <UIKit/UIKit.h>
@class DYHTabbar;
@protocol DYHTabbarDelegate<NSObject>
@optional
-(void)tabBar:(DYHTabbar *)tabBar didSelectButtonFrom:(int)from To:(int)to;
@end
@interface DYHTabbar : UIView
@property (nonatomic,weak) id<DYHTabbarDelegate> delegate;
-(void)addButtonWithATabbarItem:(UITabBarItem* )item;
@end
分析:
1.自定义的TabBar只需要能够在上面放置按钮即可,故我们继承自UIView即可。
2.自定义的TabBar上面的按钮有几个,内容如何,应该由控制器决定,故我们只需要提供API: addButtonWithATabbarItem
让控制器传递一个系统自带的TabBarItem模型给Tabbar来添加按钮
3.很明显控制器需要监听TabBar上按钮的点击,来切换子控制器,故我们定义一个代理协议,由控制器实现
接下来是TabBar的.m文件
#import "DYHTabbar.h"
#import "DYHTabbarButton.h"
@interface DYHTabbar()
@property (nonatomic,strong) NSMutableArray* buttons;
@property (nonatomic,weak) DYHTabbarButton* selectedButton;
@end
@implementation DYHTabbar
-(NSMutableArray *)buttons
{
if (_buttons == nil) {
_buttons = [NSMutableArray array];
}
return _buttons;
}
-(void)addButtonWithATabbarItem:(UITabBarItem *)item
{
DYHTabbarButton* button = [[DYHTabbarButton alloc]init];
[self addSubview:button];
button.item = item;
[button addTarget:self action:@selector(buttonClick:)forControlEvents:UIControlEventTouchDown];
[self.buttons addObject:button];
if (self.buttons.count == 1) {
[self buttonClick:button];
}
}
-(void)buttonClick:(DYHTabbarButton *)button
{
NSLog(@"buttonClick:");
if ([self.delegaterespondsToSelector:@selector(tabBar:didSelectButtonFrom:To:)]) {
[self.delegate tabBar:selfdidSelectButtonFrom:self.selectedButton.tag To:button.tag];
}
self.selectedButton.selected = NO;
button.selected = YES;
self.selectedButton = button;
}
-(void)layoutSubviews
{
[super layoutSubviews];
CGFloat tabBarH = self.frame.size.height;
CGFloat tabBarW = self.frame.size.width;
CGFloat buttonW = tabBarW / self.buttons.count;
CGFloat buttonH = tabBarH;
CGFloat buttonY = 0;
for (int i = 0; i<self.buttons.count ; i++) {
//取出按钮
DYHTabbarButton* button = self.buttons[i];
//设置按钮的frame
CGFloat buttonX = i*buttonW;
button.frame = CGRectMake(buttonX, buttonY, buttonW, buttonH);
//绑定TAG
button.tag = i;
}
}
@end
分析:
1.tabbar需要访问内部的按钮,故有数组buttons
2.tabbar需要知道当前被选中的按钮,故有属性selectedButton
3.buttons是mutableArray,故进行懒加载
4.addbutton,添加按钮,接收UITabBarItem模型以设置按钮属性,默认点击第一个按钮,并且每个按钮被点击通知Tabbar
5.buttonClick:按钮被点击的回调方法,由于Tabbar本身没有改变控制器的能力,故必须通知其控制器,执行代理方法,整个消息路径是 button被点击--(通知)-->Tabbar--(通知)-->控制器->执行切换控制器操作。另外,在这个方法中需要设置按钮的点击状态(样式变化)以及Tabbar的selectedButton
6.layoutSubviews,在TabBar加载时自动调用,算出按钮的frame,很简单的算法,要注意的是这里需要set一下按钮的tag值,用来标记这是第几个按钮。
接下来我们来看看自定义的button,由于我们自定义的整个Tabbar本身是透明的,故实际显示的效果全看我们自定义的Button。
.h文件
#import <UIKit/UIKit.h>
@interface DYHTabbarButton : UIButton
@property (nonatomic,strong) UITabBarItem* item;
@end
分析:
1.没什么好说的,一个模型属性来接收模型
.m文件
#import "DYHTabbarButton.h"
//图标比例
#define DYHTabBarButtonImageRatio 0.7
//按钮默认文字颜色
#define DYHTabBarButtonTitleColor (iOS7 ? [UIColor blackColor] : [UIColor whiteColor])
//选中文字颜色
#define DYHTabBarButtonTitleSlectedColor (iOS7 ? DYHColor(234,103,0) : DYHColor(248,139,0))
@implementation DYHTabbarButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.imageView.contentMode =UIViewContentModeCenter;
self.titleLabel.textAlignment = NSTextAlignmentCenter;
self.titleLabel.font = [UIFont systemFontOfSize:10];
[self setTitleColor:DYHTabBarButtonTitleColorforState:UIControlStateNormal];
[self setTitleColor:DYHTabBarButtonTitleSlectedColorforState:UIControlStateSelected];
}
return self;
}
-(void)setItem:(UITabBarItem *)item
{
_item = item;
//KVO 监听属性
[item addObserver:self forKeyPath:@"badgeValue"options:0 context:nil];
[item addObserver:self forKeyPath:@"title" options:0context:nil];
[item addObserver:self forKeyPath:@"image" options:0context:nil];
[item addObserver:self forKeyPath:@"selectedImage"options:0 context:nil];
[self observeValueForKeyPath:nil ofObject:nil change:nilcontext:nil];
}
//监听到某个对象的属性改变了就会调用
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
//设置文字和图片
[self setTitle:self.item.title forState:UIControlStateNormal];
[self setImage:self.item.imageforState:UIControlStateNormal];
[self setImage:self.item.selectedImageforState:UIControlStateSelected];
}
-(void)dealloc
{
[self removeObserver:self forKeyPath:@"badgeValue"];
[self removeObserver:self forKeyPath:@"title"];
[self removeObserver:self forKeyPath:@"image"];
[self removeObserver:self forKeyPath:@"selectedImage"];
}
-(CGRect)titleRectForContentRect:(CGRect)contentRect
{
CGFloat titleY =contentRect.size.height*DYHTabBarButtonImageRatio;
CGFloat titleW =contentRect.size.width;
return CGRectMake(0, titleY, titleW , contentRect.size.height - titleY);
}
-(CGRect)imageRectForContentRect:(CGRect)contentRect
{
CGFloat imageW = contentRect.size.width;
CGFloat imageH = contentRect.size.height*DYHTabBarButtonImageRatio;
return CGRectMake(0, 0, imageW, imageH);
}
@end
分析: 1.init没什么好说的,设置一些颜色字体,根据需求来
2.setItem,读取模型数据的方法,需要注意的是这里我们使Item监听自己的值改变,为的是完善逻辑,使用button.item.属性同样也能更改button的样式;另外我们默认调用一次值改变回调方法,初始化。
3.值改变回调方法,没什么好说的,根据值改变设置按钮属性
4.dealloc方法,在这个对象被销毁时停止监听
5,6用来设置按钮上的文字和图片的显示样式
至此我们的自定义Tabbar完成了,不过controller在使用的时候还有一些要注意的(我们默认使用UITabBarController及其子类):
1.需要写如下代码
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
for (UIView *child in self.tabBar.subviews) {
if ([child isKindOfClass:[UIControl class]]) {
[child removeFro
}mSuperview];
}
}
因为tabBarController内部本身有一个TabBar,执行addChildController操作后默认tabbar也会加上按钮,需要移除这些按钮,否则会造成显示错误。
2.实现代理方法:
-(void)tabBar:(DYHTabbar *)tabBar didSelectButtonFrom:(int)from To:(int)to
{
self.selectedIndex = to;
}
由于tabBarController本身重写了selectedIndex的set方法,并且我们传的值恰好与子控制器顺序相对应,故只需要这一句代码即可完成切换