iOS OC-自定义TabBar TabBarViewController

2,762 阅读4分钟

  前段时间,我们产品同学又提了个需求。咳咳,就是想要服务器动态控制tabBar的图片和文字,而且要兼容文字没有的情况下图片撑满,而且图片的显示范围固定。但是系统自带的tabBarItem我试了下是不能固定图片显示范围的,它是根据图片的大小来适应的。所有我就自定义了一套,最终效果如下(最后有demo地址):

QQ20180726-134906-HD.gif

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:github.com/SionChen/CY…