开发中,经常会遇到各种各样的奇葩设计要求,因为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 = ;
    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];

    ; 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, , 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(, [UIScreen mainScreen].bounds.size.height - , [UIScreen mainScreen].bounds.size.width, );
    // 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

自定义UITabBar的两种方式的更多相关文章

  1. iOS 自定义layer的两种方式

    在iOS中,你能看得见摸得着的东西基本都是UIView,比如一个按钮,一个标签,一个文本输入框,这些都是UIView: 其实UIView之所以能显示在屏幕上,完全是因为它内部的一个图层 在创建UIVi ...

  2. EntityFramework Core 2.0自定义标量函数两种方式

    前言 上一节我们讲完原始查询如何防止SQL注入问题同时并提供了几种方式.本节我们继续来讲讲EF Core 2.0中的新特性自定义标量函数. 自定义标量函数两种方式 在EF Core 2.0中我们可以将 ...

  3. UITabBar背景、icon图标颜色、被选中背景设置以及隐藏UITabBar的两种方式

    一.对UITabBar背景和icon图标的一些设置 (1)因为直接给UITabBar设置的背景颜色显示的不纯,半透明的感觉,所以,有时候我们可以直接利用纯色的图片作为背景达到想要的效果: (2)给ic ...

  4. 【iOS开发-31】UITabBar背景、icon图标颜色、被选中背景设置以及隐藏UITabBar的两种方式

    一.对UITabBar背景和icon图标的一些设置 (1)由于直接给UITabBar设置的背景颜色显示的不纯.半透明的感觉,所以,有时候我们能够直接利用纯色的图片作为背景达到想要的效果. (2)给ic ...

  5. android 自定义radiogroup的两种方式

    这里先备注下 listview+radiobutton实现  浅显易懂 http://www.haolizi.net/example/view_3312.html 在radiogoup原生态源码的基础 ...

  6. Hibernate查询返回自定义VO的两种方式

    说明:createQuery用的hql语句进行查询,createSQLQuery用sql语句查询: 前者以hibernate生成的Bean为对象装入list返回:后者则是以对象数组进行存储: 一.通过 ...

  7. Android ScrollView监听滑动到顶部和底部的两种方式(你可能不知道的细节)

    Android ScrollView监听滑动到顶部和底部,虽然网上很多资料都有说,但是不全,而且有些细节没说清楚 使用场景: 1. 做一些复杂动画的时候,需要动态判断当前的ScrollView是否滚动 ...

  8. 在基于MVC的Web项目中使用Web API和直接连接两种方式混合式接入

    在我之前介绍的混合式开发框架中,其界面是基于Winform的实现方式,后台使用Web API.WCF服务以及直接连接数据库的几种方式混合式接入,在Web项目中我们也可以采用这种方式实现混合式的接入方式 ...

  9. 《连载 | 物联网框架ServerSuperIO教程》- 10.持续传输大块数据流的两种方式(如:文件)

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

随机推荐

  1. rem、px、em之间的区别以及网页响应式设计写法

    个人收藏用,转载自:http://www.w3cplus.com/css3/define-font-size-with-css3-rem 在Web中使用什么单位来定义页面的字体大小,至今天为止都还在激 ...

  2. 关于web api 2 客户端请求Post

    (一).客户端的部分代码[需要添加NuGet程序包] 附:Client 声明方法 HttpClient client = new HttpClient(); client.BaseAddress = ...

  3. [LeetCode]题解(python):035-Search Insert Position

    题目来源 https://leetcode.com/problems/search-insert-position/ Given a sorted array and a target value, ...

  4. DFS入门之二---DFS求连通块

    用DFS求连通块也是比较典型的问题, 求多维数组连通块的过程也称为--“种子填充”. 我们给每次遍历过的连通块加上编号, 这样就可以避免一个格子访问多次.比较典型的问题是”八连块问题“.即任意两格子所 ...

  5. 8种CSS清除浮动的方法优缺点分析

    为什么清除CSS浮动这么难? 因为浮动会使当前标签产生向上浮的效果,同时会影响到前后标签.父级标签的位置及 width height 属性.而且同样的代码,在各种浏览器中显示效果也有可能不相同,这样让 ...

  6. [Angualr 2] Watch for changes

    You can watch for form / control changes by using .valueChanges.observe({...}): this.sku.valueChange ...

  7. IOS快速开发之常量定义

    ---恢复内容开始--- 在IOS开发中,有一些方法常常需要用的,但是有很长的方法名,这造成了代码长,写起来累,我们可以通过宏定义了解决这些问题 比如说在代码布局的时候会遇上这样的问题,我们要获取上面 ...

  8. Unix系统解压tar包时出现@LongLink错误

    Unix系统上使用tar命令解压tar包后,多了一个@LongLink的文件,并且原来的tar包解压后不完整.网上查了下,原因是AIX系统上tar命令自身的一个缺陷.解决办法:把该tar包上传到lin ...

  9. 关于bootstrap--表格(tr的各种样式)

    只需要<tr class="active">就可以用active样式. 特别提示:除了”.active”之外,其他四个类名和”.table-hover”配合使用时,Bo ...

  10. Stripe

    Description Once Bob took a paper stripe of n squares (the height of the stripe is 1 square). In eac ...