前段时间,我们产品同学又提了个需求。咳咳,就是想要服务器动态控制tabBar的图片和文字,而且要兼容文字没有的情况下图片撑满,而且图片的显示范围固定。但是系统自带的tabBarItem我试了下是不能固定图片显示范围的,它是根据图片的大小来适应的。所有我就自定义了一套,最终效果如下(最后有demo地址):
1、自定义tabBarItem
@interface CYXTabBarItem : UIView
/*未选中的图片*/
@property (nonatomic,strong) UIImage *image;
/*选中的图片*/
@property (nonatomic,strong) UIImage *selectedImage;
/*标题*/
@property (nonatomic,copy) NSString *title;
/*是否是选中*/
@property (nonatomic,assign) BOOL selected;
/*最大图片尺寸*/
@property (nonatomic,assign) CGSize maxImageSize;
/*最小图片尺寸*/
@property (nonatomic,assign) CGSize minImageSize;
/*文字颜色*/
@property (nonatomic,strong) UIColor *titleColor;
/*选中的文字颜色*/
@property (nonatomic,strong) UIColor *selectedTitleColor;
/*点击*/
@property (nonatomic,strong) void(^selectBlock)(void);
/*更新布局方法 设置完之后记得调用*/
-(void)updateImageAndTitle;
@end
implementation:
@interface CYXTabBarItem()
/*图片*/
@property (nonatomic,strong) UIImageView *imageView;
/*文字*/
@property (nonatomic,strong) UILabel *titleLabel;
@end
@implementation CYXTabBarItem
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.imageView];
[self addSubview:self.titleLabel];
[self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(11);
make.left.and.right.equalTo(self);
make.top.equalTo(self).offset(35);
}];
__weak __typeof(self) _self = self;
[self setTapActionWithBlock:^{
if (_self.selectBlock) {
_self.selectBlock();
}
}];
}
return self;
}
/*更新布局方法 设置完之后记得调用*/
-(void)updateImageAndTitle{
self.titleLabel.text = self.title;
if (self.selected) {
self.titleLabel.font = [UIFont systemFontOfSize:10.0];
self.titleLabel.textColor = self.selectedTitleColor;
if (self.selectedImage) {
self.imageView.image = self.selectedImage;
}
}else{
self.titleLabel.font = [UIFont boldSystemFontOfSize:10.0];
self.titleLabel.textColor = self.titleColor;
if (self.image) {
self.imageView.image = self.image;
}
}
if ([self.title length]) {
[self.imageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.height.and.width.mas_equalTo(22);
make.centerX.equalTo(self);
make.top.equalTo(self).offset(5);
}];
}else{
[self.imageView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.height.and.width.mas_equalTo(40);
make.centerX.equalTo(self);
make.top.equalTo(self).offset(5);
}];
}
}
#pragma mark ---G
-(UIImageView*)imageView{
if(!_imageView){
_imageView = [[UIImageView alloc] init];
}
return _imageView;
}
-(UILabel*)titleLabel{
if(!_titleLabel){
_titleLabel = [[UILabel alloc] init];
_titleLabel.font = [UIFont systemFontOfSize:10];
_titleLabel.textColor = [UIColor grayColor];
_titleLabel.textAlignment = NSTextAlignmentCenter;
}
return _titleLabel;
}
2、自定义tabBarView
@interface CYXBarView : UIView
/*初始化按钮*/
-(void)initButtonWithViewControllers:(NSArray<UIViewController *> * )viewControllers;
/*最大图片尺寸*/
@property (nonatomic,assign) CGSize maxImageSize;
/*最小图片尺寸*/
@property (nonatomic,assign) CGSize minImageSize;
/*文字颜色*/
@property (nonatomic,strong) UIColor *titleColor;
/*选中的文字颜色*/
@property (nonatomic,strong) UIColor *selectedTitleColor;
/*点击*/
@property (nonatomic,strong) void(^selectBlock)(NSInteger index);
@property (nonatomic, assign) NSInteger selectIndex;
@end
implementation:
@interface CYXBarView()
@property (nonatomic,strong) NSMutableArray<CYXTabBarItem *> *items;
@end
@implementation CYXBarView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.maxImageSize = CGSizeMake(40, 40);
self.minImageSize = CGSizeMake(20, 20);
self.titleColor = [UIColor grayColor];
self.selectedTitleColor = [UIColor redColor];
self.items = [NSMutableArray new];
}
return self;
}
-(void)initButtonWithViewControllers:(NSArray<UIViewController *> * )viewControllers{
[self.items removeAllObjects];
for (UIView * view in self.subviews) {
[view removeFromSuperview];
}
CGFloat itemWidth =screenWidth/viewControllers.count;
for (int i= 0; i<viewControllers.count; i++) {
CYXTabBarItem * item = [[CYXTabBarItem alloc] init];
item.maxImageSize = self.maxImageSize;
item.minImageSize = self.minImageSize;
item.titleColor = self.titleColor;
item.selectedTitleColor = self.selectedTitleColor;
UIViewController * viewController = viewControllers[i];
item.title = [viewController.tabBarItem.title length]?viewController.tabBarItem.title:viewController.title;
item.selectedImage = viewController.tabBarItem.selectedImage;
item.image = viewController.tabBarItem.image;
[self addSubview:item];
[item mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.mas_equalTo(itemWidth);
make.height.equalTo(self);
make.top.equalTo(self);
make.left.mas_equalTo(i*itemWidth);
}];
[self.items addObject:item];
__weak __typeof(self) _self = self;
item.selectBlock = ^{
_self.selectIndex = i;
if (_self.selectBlock) {
_self.selectBlock(_self.selectIndex);
}
};
[item updateImageAndTitle];
}
self.selectIndex = 0;
}
- (void)setSelectIndex:(NSInteger)selectIndex
{
if (![self.items count]||selectIndex>=[self.items count]) {return;}
// 先把上次选择的item设置为可用
CYXTabBarItem *lastItem = self.items[_selectIndex];
lastItem.selected = NO;
// 再把这次选择的item设置为不可用
CYXTabBarItem *item = self.items[selectIndex];
item.selected = YES;
_selectIndex = selectIndex;
[lastItem updateImageAndTitle];
[item updateImageAndTitle];
}
@end
2、自定义tabBar
@class CYXBarView;
@interface CYXTabBar : UITabBar
@property (nonatomic,strong) CYXBarView *tabBarView;
@end
implementation:
@interface CYXTabBar ()
@end
@implementation CYXTabBar
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.tabBarView];
[self.tabBarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.and.right.and.left.and.bottom.equalTo(self);
}];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
// 把tabBarView带到最前面,覆盖tabBar的内容
[self bringSubviewToFront:self.tabBarView];
/*隐藏原来的*/
for (UIView *view in self.subviews) {
if ([view isKindOfClass:NSClassFromString(@"UITabBarButton")]) {
view.hidden = YES;
}
}
}
// 重写hitTest方法,让超出tabBar部分也能响应事件
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if (self.clipsToBounds || self.hidden || (self.alpha == 0.f)) {
return nil;
}
UIView *result = [super hitTest:point withEvent:event];
// 如果事件发生在tabbar里面直接返回
if (result) {
return result;
}
// 这里遍历那些超出的部分就可以了,不过这么写比较通用。
for (UIView *subview in self.tabBarView.subviews) {
// 把这个坐标从tabbar的坐标系转为subview的坐标系
CGPoint subPoint = [subview convertPoint:point fromView:self];
result = [subview hitTest:subPoint withEvent:event];
// 如果事件发生在subView里就返回
if (result) {
return result;
}
}
return nil;
}
#pragma mark ---G
-(CYXBarView*)tabBarView{
if(!_tabBarView){
_tabBarView = [[CYXBarView alloc] init];
}
return _tabBarView;
}
2、自定义UITabBarController
@interface CYXTabBarViewController : UITabBarController
@end
implementation:
@interface CYXTabBarViewController ()
@property (nonatomic,strong) CYXTabBar *customTabBar;
@end
@implementation CYXTabBarViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setValue:self.customTabBar forKey:@"tabBar"];
[self initViewControllers];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.selectedIndex = 3;
});
}
-(void)initViewControllers{
NSMutableArray * viewControllers = [NSMutableArray new];
UIViewController *homeVC = [[UIViewController alloc] init];
homeVC.view.backgroundColor = [UIColor redColor];
[viewControllers addObject:[self addChildViewController:homeVC title:@"首页" imageNamed:@"tabBar_home"]];
UIViewController *expoVC = [[UIViewController alloc] init];
expoVC.view.backgroundColor = [UIColor yellowColor];
[viewControllers addObject:[self addChildViewController:expoVC title:@"家博会" imageNamed:@"tabBar_activity"]];
UIViewController *activityVC = [[UIViewController alloc] init];
activityVC.view.backgroundColor = [UIColor yellowColor];
[viewControllers addObject:[self addChildViewController:activityVC title:@"" imageNamed:@"tabBar_activity"]];
UIViewController *findVC = [[UIViewController alloc] init];
findVC.view.backgroundColor = [UIColor blueColor];
[viewControllers addObject:[self addChildViewController:findVC title:@"发现" imageNamed:@"tabBar_find"]];
UIViewController *mineVC = [[UIViewController alloc] init];
mineVC.view.backgroundColor = [UIColor greenColor];
[viewControllers addObject:[self addChildViewController:mineVC title:@"我的" imageNamed:@"tabBar_mine"]];
self.viewControllers = viewControllers;
}
// 添加某个 childViewController
- (UINavigationController *)addChildViewController:(UIViewController *)vc title:(NSString *)title imageNamed:(NSString *)imageNamed
{
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
// 如果同时有navigationbar 和 tabbar的时候最好分别设置它们的title
vc.navigationItem.title = title;
nav.tabBarItem.title = title;
nav.tabBarItem.image = [UIImage imageNamed:imageNamed];
nav.tabBarItem.selectedImage = [UIImage imageNamed:@"jmtIconBg"];
return nav;
}
-(void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers{
[super setViewControllers:viewControllers];
[self.customTabBar.tabBarView initButtonWithViewControllers:viewControllers];
}
-(void)setSelectedIndex:(NSUInteger)selectedIndex{
[super setSelectedIndex:selectedIndex];
self.customTabBar.tabBarView.selectIndex = selectedIndex;
}
#pragma mark ---G
-(CYXTabBar*)customTabBar{
if(!_customTabBar){
_customTabBar = [[CYXTabBar alloc] init];
__weak __typeof(self) _self = self;
_customTabBar.tabBarView.selectBlock = ^(NSInteger index) {
_self.selectedIndex = index;
};
}
return _customTabBar;
}
Appdelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = [[CYXTabBarViewController alloc] init];
// 设置这个窗口有主窗口并显示
[self.window makeKeyAndVisible];
return YES;
}
这样就实现上图所示效果图了,高度自定义化,可随意修改。欢迎讨论指教
demo:https://github.com/SionChen/CYXCustomTabBarController