iOS OC-自定義TabBar TabBarViewController

SionChen發表於2019-04-19

  前段時間,我們產品同學又提了個需求。咳咳,就是想要伺服器動態控制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…

相關文章