自定義UITabBar的兩種方式


開發中,經常會遇到各種各樣的奇葩設計要求,因為apple提供的UITabBar樣式單一,只是簡單的"圖片+文字"樣式,高度49又不可以改變。自定義UITabBar成為了唯一的出路。下面我就列舉開發中我經常用到的兩種自定義UITabBar的方式,並且通過比較他們的不同之處,能夠知道何時用何種方式自定義UITabBar。

方式一:

這是真正意義上的自定義UITabBar,因為這種方式需要繼承自UITabBar,但是缺點也很明顯,高度永遠是49,實際開發的項目中的tabBar如果和原生的UITabBar相差不大,可以考慮這種方式。廢話少說,直接上代碼。

#import <UIKit/UIKit.h>

@interface WSTabBar : UITabBar

@end

#import "WSTabBar.h"

@implementation WSTabBar


- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        // 初始化操作
    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    // 布局子控件
    for (UITabBarItem *item in self.subviews) {
//        item.title = @"標題";
//        item.image = [UIImage imageNamed:@""];
    }
    
}
@end

// UITabBarController中設置自定義tabBar - (void)setupTabBar { // self.tabBar = [[WSTabBar alloc] init]; // 不能直接賦值,只能通過KVC方式設置tabBar [self setValue:[[WSTabBar alloc] init] forKeyPath:@"tabBar"]; }

以上這種方式實際上還是tabBar,只不過我們可以在tabBar的initWithFrame:方法中給tabBar做一些初始化操作,比如添加一個不同於其他item的button。另外也可以在layoutSubviews中設置每個UITabBarItem和我們自己添加的button的frame。

方式二:

第二種方式是自定義UIView代替tabBar,然后把UIView添加到tabBar控制器上。監聽自定義UIView上每個按鈕的點擊,然后跳轉到指定的tabBar子控制器。這種方式的缺點是,比第一種稍稍復雜了一點點,優點是靈活度高,我們可以天馬行空的把tabBar設計成任何樣式。廢話少說,直接上代碼。

以下是自定義UITabBar:

#import <UIKit/UIKit.h>
@class WSTabBar;

@protocol WSTabBarDelegate <NSObject>
/** 控制器遵守該協議后,用於傳遞tabBar當前選中的按鈕的索引給控制器 */
- (void)tabBar:(WSTabBar *)tabBar withSelectedIndex:(NSInteger)selectedIndex;

@end

@interface WSTabBar : UIView
/** tabBar的item數組 */
@property (nonatomic,strong) NSArray *items;
/** tabBar所屬的tabBarController */
@property (nonatomic,strong) UITabBarController *tabBarController;

/** block */
@property (nonatomic,strong) void(^selectedIndexBlock)(NSInteger);

// 代理
@property (nonatomic,weak) id<WSTabBarDelegate> delegate;

@end


#import "WSTabBar.h"

@interface WSTabBar ()
/** 當前選中的按鈕 */
@property(nonatomic,strong) UIButton *selectedBtn;

@end

@implementation WSTabBar
//- (instancetype)initWithFrame:(CGRect)frame
//{
//    if (self = [super initWithFrame:frame]) {
//        
//    }
//    
//    return self;
//}

- (void)setItems:(NSArray *)items
{
    _items = items; // 千萬不要漏掉這一句啊!漏掉后,會導致_items為nil
    NSInteger index = 0;
    for (UITabBarItem *item in items) {
        UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
        [btn setBackgroundImage:item.image forState:UIControlStateNormal];
        [btn setBackgroundImage:item.selectedImage forState:UIControlStateSelected];
        
        btn.tag = index;
        ++index;
        [btn addTarget:self action:@selector(btnDidOnClick:) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:btn];
    }
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    for (int i = 0; i < self.items.count; i++) {
        UIButton *btn = self.subviews[i];
        CGFloat width = self.frame.size.width / self.items.count;
        CGFloat height = self.frame.size.height;
        
        btn.frame = CGRectMake(i * width, 0, width, height);
        
    }
}
- (void)btnDidOnClick:(UIButton *)btn
{
    // 三部曲
    self.selectedBtn.selected = NO; // 上一個按鈕取消選中
    btn.selected = YES; // 當前按鈕設置選中
    self.selectedBtn = btn; // 當前按鈕賦值給selectedBtn
    
    // 5種方法
    // 1.tabBarController屬性
    /*
    self.tabBarController.selectedIndex = btn.tag;
     */
    
    // 2.通知
    /*
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    dict[@"selectedIndex"] = @(btn.tag);
    [[NSNotificationCenter defaultCenter] postNotificationName:@"changeSelectedIndexNotification" object:self userInfo:dict];
     */
    
    // 3.KVO 控制器或者tabBar監聽selectedBtn.tag的變化
    
    // 4.block
    /*
    if (self.selectedIndexBlock) {
        self.selectedIndexBlock(btn.tag);
    }
    */
    
    // 5.代理
    if ([self.delegate respondsToSelector:@selector(tabBar:withSelectedIndex:)]) {
        [self.delegate tabBar:self withSelectedIndex:btn.tag];
    }
}

- (void)dealloc
{

}

以下是使用UITabBar:

#import <UIKit/UIKit.h>

@interface WSTabBarController : UITabBarController

@end


#import "WSTabBarController.h"
#import "WSTabBar.h"
#import "OneChildViwController.h"
#import "TwoChildViwController.h"
#import "ThreeChildViwController.h"
#import "FourChildViwController.h"
#import "FiveChildViwController.h"

@interface WSTabBarController ()<WSTabBarDelegate>
@property(nonatomic,strong) NSMutableArray *items;
@property(nonatomic,strong) WSTabBar *WSTabBar;

@end

@implementation WSTabBarController

- (NSMutableArray *)items
{
    if (!_items) {
        _items = [NSMutableArray array];
    }
    return _items;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    [self setupChildVC];
    [self setupTabBar];
    
    // 2.注冊監聽通知
//    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeSelectedIndex:) name:@"changeSelectedIndexNotification" object:nil];
    
//    // 3.KVO
//    [self addObserver:self forKeyPath:@"WSTabBar.selectedBtn.tag" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

    
}

- (void)changeSelectedIndex:(NSNotification *)noti
{
    self.selectedIndex = [noti.userInfo[@"selectedIndex"] integerValue];
}

- (void)dealloc
{
    // 移除通知監聽
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    // 移除KVO監聽
    [self removeObserver:self forKeyPath:@"WSTabBar.selectedBtn.tag"];
}
- (void)setupChildVC
{
    OneChildViwController *oneVC = [[OneChildViwController alloc] init];
    TwoChildViwController *twoVC = [[TwoChildViwController alloc] init];
    ThreeChildViwController *threeVC = [[ThreeChildViwController alloc] init];
    FourChildViwController *fourVC = [[FourChildViwController alloc] init];
    FiveChildViwController *fiveVC = [[FiveChildViwController alloc] init];

    [self setupChildVC:oneVC image:@"TabBar_Arena_new" selectedImage:@"TabBar_Arena_selected_new" title:@"競技場"];
    [self setupChildVC:twoVC image:@"TabBar_Discovery_new" selectedImage:@"TabBar_Discovery_selected_new" title:@"發現"];
    [self setupChildVC:threeVC image:@"TabBar_History_new" selectedImage:@"TabBar_History_selected_new" title:@"開獎信息"];
    [self setupChildVC:fourVC image:@"TabBar_LotteryHall_new" selectedImage:@"TabBar_LotteryHall_selected_new" title:@"購彩大廳"];
    [self setupChildVC:fiveVC image:@"TabBar_MyLottery_new" selectedImage:@"TabBar_MyLottery_selected_new" title:@"我的彩票"];
    
}

- (void)setupChildVC:(UIViewController *)childVC image:(NSString *)image selectedImage:(NSString *)selectedImage title:(NSString *)title
{
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:childVC];
    
    childVC.tabBarItem.image = [UIImage imageNamed:image];
    childVC.tabBarItem.selectedImage = [UIImage imageNamed:selectedImage];
    childVC.tabBarItem.title = title;
    [self.items addObject:childVC.tabBarItem];
    
    [self addChildViewController:nav];
}

- (void)setupTabBar
{
    WSTabBar *tabBar = [[WSTabBar alloc] init];
    tabBar.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height - 49, [UIScreen mainScreen].bounds.size.width, 49);
    // iphone 4、5、6 tabBar高度為49
    
    // iPhone 4、5、6 UINavigationBar高度為44
    // iphone 4、5、6 UINavigationBarBackGround高度為64 所以會有狀態欄被導航欄同化的現象
    
    tabBar.items = self.items; // 不能 tabBar.items = self.tabBar.items;
    
    tabBar.backgroundColor = [UIColor orangeColor];
    [self.view addSubview:tabBar];
    // 1.WSTabBar的tabBarController屬性
//     tabBar.tabBarController = self;
    
    // 3.KVO(tabBar監聽)
//    [tabBar addObserver:self forKeyPath:@"selectedBtn.tag" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    
    // 3.KVO(讓控制器監聽)
//    self.WSTabBar = tabBar;
//    [self addObserver:self forKeyPath:@"WSTabBar.selectedBtn.tag" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    
    // 4.block方式
//    tabBar.selectedIndexBlock = ^(NSInteger selectedIndex){
//        self.selectedIndex = selectedIndex;
//    };
    
    // 5.代理
    tabBar.delegate = self;
    
}

// KVO監聽到鍵值變化
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSInteger new = [change[NSKeyValueChangeNewKey] integerValue];
    self.selectedIndex = new;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    for (UIView *subView in self.tabBar.subviews) {
        if (![subView isKindOfClass:[WSTabBar class]]) {
            // 移除原生tabBar上的item
            [subView removeFromSuperview];
        }
    }
}

#pragma mark - WSTabBarDelegate
- (void)tabBar:(WSTabBar *)tabBar withSelectedIndex:(NSInteger)selectedIndex
{
    self.selectedIndex = selectedIndex;
}
@end

以上第二種方式,筆者使用了5中方式在自定義的tabBar和tabBarController之間傳值,實現tabBar子控制器的切換。

詳細代碼demo:https://github.com/nlgb/WSTabBar/tree/master/WSTabBar

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM