tabbar本身的barbuttonitem(bbi)在我看来有很多不需要用到的属性变量,而且在开发中有时候会需要用到自定义的bbi,网上普遍做法是隐藏原有的tabbar然后在其原来的位置创建一个新的uiview加上一堆按钮,但是这种方法在我们使用导航控制器需要隐藏tabbar的时候非常的不方便。笔者的做法是在初始化子控制器数组后移除tabbar上面所有的按钮对象,然后在上面加上自定义的所有按钮。其中,自定义按钮对象继承自uiview,原因是如果继承自uibutton或者bbi会有很多不需要的属性变量。
自定义类LXDTabBarButtonItem.h
<span style="font-size:14px;">#import <UIKit/UIKit.h>
/**
* view的点击状态枚举
*/
typedef NS_ENUM(NSUInteger, LXDViewState){
ViewStateNormal = 0, //正常状态
ViewStateSelected //选中状态
};
@class LXDTabBarButtonItem;
/**
* 点击回调函数块
*/
typedef void(^LXDClickViewHandler)(LXDTabBarButtonItem * barItem);
/**
* 自定义的tabbarItem
*/
@interface LXDTabBarButtonItem : UIView
/**
* 当前view是否处于选中状态
*/
@property (nonatomic, readonly, getter=isSelected)BOOL selected;
/**
* 设置view的选中状态
*
* @param selected 新的状态
*/
- (void)setSelected:(BOOL)selected;
/**
* 设置显示在view上面的文本内容
*
* @param title 要显示的文本内容
* @param state 显示的状态
*/
- (void)setTitle: (NSString *)title forState: (LXDViewState)state;
/**
* 设置view在不同状态下的图片
*
* @param image 要显示的图片
* @param state 显示的状态
*/
- (void)setImage: (UIImage *)image forState: (LXDViewState)state;
/**
* 增加view的点击回调函数
*
* @param clickHandler 回调函数块
*/
- (void)addClickHandler: (LXDClickViewHandler)clickHandler;
@end</span>
虽然我们的bbi是自定义的,但是在命名上尽可能的使用button相关的函数名称,方便我们操作。
对比原有的bbi,自定义的bbi明显要更加的灵活。灵活度可以表现在下面两点:
1、我们可以在手势点击的回调函数中给按钮加入CA框架下的动画效果,使得自定义的tabbar在交互中的体验更加良好
2、通过block回调我们可以更加灵活的处理点击事件,完成更多的功能
LXDTabBarButtonItem.m
<span style="font-size:14px;">#import "LXDTabBarButtonItem.h"
#import "LXDGlobalDefine.h"
@interface LXDTabBarButtonItem ()
/**
* 用来存储不同状态的显示图片
*/
@property (nonatomic, strong) NSMutableDictionary * images;
/**
* 用来存储不同状态下的文本内容
*/
@property (nonatomic, strong) NSMutableDictionary * titles;
/**
* 显示的图片
*/
@property (nonatomic, strong) UIImageView * itemImage;
/**
* 当前view的状态
*/
@property (nonatomic, assign) LXDViewState viewState;
/**
* 点击屏幕的回调事件
*/
@property (nonatomic, copy) LXDClickViewHandler clickHandler;
/**
* 显示文本内容
*/
@property (nonatomic, strong) UILabel * titleLabel;
@end
@implementation LXDTabBarButtonItem
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame: frame]) {
/**
* 点按手势
*/
UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(clickView)];
[self addGestureRecognizer: tapGesture];
_images = [NSMutableDictionary new];
}
return self;
}
/**
* 设置view的选中状态
*
* @param selected 新的状态
*/
- (void)setSelected:(BOOL)selected
{
_selected = selected;
_viewState = selected;
/**
* 取出对应状态的图片显示
*/
NSString * stateKey = [NSString stringWithFormat: @"%lu", _viewState];
if (_images[stateKey]) {
_itemImage.image = _images[stateKey];
}
/**
* 取出对应状态的文本内容
*/
if (_titles[stateKey]) {
_titleLabel.text = _titles[stateKey];
}
[self resetTitleColor];
}
/**
* 设置显示在view上面的文本内容
*
* @param title 要显示的文本内容
* @param state 显示的状态
*/
- (void)setTitle:(NSString *)title forState:(LXDViewState)state
{
/**
* 惰性初始化
*/
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] initWithFrame: CGRectMake(20, LXD_TABBAR_HEIGHT - 13, self.frame.size.width - 40, 8)];
_titleLabel.font = [UIFont systemFontOfSize: 9];
_titleLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview: _titleLabel];
[self resetTitleColor];
}
[_titles setValue: title forKey: [NSString stringWithFormat: @"%lu", (unsigned long)state]];
if (_viewState == state) {
_titleLabel.text = title;
}
}
/**
* 设置view在不同状态下的图片
*
* @param image 要显示的图片
* @param state 显示的状态
*/
- (void)setImage:(UIImage *)image forState:(LXDViewState)state
{
[_images setValue: image forKey: [NSString stringWithFormat: @"%lu", (unsigned long)state]];
/**
* 惰性初始化
*/
if (!_itemImage) {
_itemImage = [[UIImageView alloc] initWithFrame: self.bounds];
[self addSubview: _itemImage];
[self sendSubviewToBack: _itemImage];
}
if (state == _viewState) {
_itemImage.image = image;
}
}
/**
* 增加view的点击回调函数
*
* @param clickHandler 回调函数块
*/
- (void)addClickHandler:(LXDClickViewHandler)clickHandler
{
self.clickHandler = clickHandler;
}
#pragma mark -- 私有方法 -- Private methods
/**
* 点按view时回调
*/
- (void)clickView
{
if (self.selected) {
return;
}
[self setSelected: !self.selected];
[self resetTitleColor];
/**
* 如果有回调函数,那么执行
*/
if (_clickHandler) {
_clickHandler(self);
}
}
/**
* 重新设置文本的颜色
*/
- (void)resetTitleColor
{
if (_viewState) {
_titleLabel.textColor = [UIColor orangeColor];
} else {
_titleLabel.textColor = [UIColor whiteColor];
}
}</span>
在实现文件中,我们对存储图片跟文本信息的字典进行了惰性初始化(延迟创建),这么做有两个好处。一是当将部分操作延后执行可以提高视图切换的速度。二是如果用户不需要相关属性,可以减少内存的损耗。
自定义部分到这里就结束了,接着是怎么删除tabbar上面的按钮控件,然后替换成自定义的按钮操作。必须要注意的是这部分操作保证在viewControllers的数组创建结束后才能执行删除的操作。
删除加上替换的代码:
<span style="font-size:14px;">- (void)createCustomTabBar
{
/**
* 先移除系统所有在tabbar上的子视图
*/
for (id subview in self.tabBar.subviews) {
if ([subview isKindOfClass: NSClassFromString(@"UITabBarButton")]) {
[subview removeFromSuperview];
}
}
NSArray * images = @[@"tab_cookbook", @"tab_explore", @"tab_plaza", @"tab_aboutme"];
NSArray * titles = @[@"菜单", @"发现", @"卖汤汤", @"我的"];
/**
* 添加自定义的tabbarItem
*/
NSInteger count = self.viewControllers.count;
CGFloat itemWidth = LXD_SCREEN_WIDTH / count;
for (int i = 0; i < count; i++) {
LXDTabBarButtonItem * barItem = [[LXDTabBarButtonItem alloc] initWithFrame: CGRectMake(i * itemWidth, 0, itemWidth, LXD_TABBAR_HEIGHT)];
barItem.tag = i;
/**
* 设置item的普通状态图片跟选中状态图片
*/
[barItem setImage: [UIImage imageNamed: images[i]] forState: ViewStateNormal];
[barItem setImage: [UIImage imageNamed: [images[i] stringByAppendingString: @"_hl"]] forState: ViewStateSelected];
/**
* 设置item的文本信息
*/
[barItem setTitle: titles[i] forState: ViewStateNormal];
/**
* 添加点击时切换状态事件
*/
__weak typeof(self) weakSelf = self;
[barItem addClickHandler: ^(LXDTabBarButtonItem * barItem){
[_selectedItem setSelected: NO];
_selectedItem = barItem;
weakSelf.selectedViewController = self.viewControllers[barItem.tag];
}];
if (i == 0) {
[barItem setSelected: YES];
_selectedItem = barItem;
}
[self.tabBar addSubview: barItem];
}
}</span>
还要注意的是在写block回调函数块的时候要防止循环引用(尤其是引用了自己),其中变量_selectedItem的声明是 __weak LXDTabBarButtonItem * _selectedItem。