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

转载于:https://www.cnblogs.com/wsnb/p/5089951.html

自定义UITabBar的两种方式相关推荐

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

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

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

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

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

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

  4. Unity中制作自定义字体的两种方式

    Unity支持自定义图片字体(CustomFont),网上有很多教程,细节不尽相同,当概括起来基本就是两种方式.一是使用BMFont,导出图集和.fnt文件,再使用图集在Unity中设置得到字体.二是 ...

  5. springboot过滤字段_SpringBoot自定义过滤器的两种方式及过滤器执行顺序

    第一种 @WebFilter + @ServletComponentScan 注解 1.首先自定义过滤器 如下自定义过滤器 ReqResFilter必须实现  javax.servlet.Filter ...

  6. crontab用法 时间配置_Linux指定的时间运行自定义命令的两种方式

    如何在Linux中在指定的时间运行自定义命令 不知道大家有没有这样类似的经历,使用诸如rsync将一个大文件传输到局域网的另一个系统. 由于文件比较大,耗费的时间可能达到小时级别.这时我们可能就会想能 ...

  7. 8 时间转指定时区的时间_Linux指定的时间运行自定义命令的两种方式

    如何在Linux中在指定的时间运行自定义命令 不知道大家有没有这样类似的经历,使用诸如rsync将一个大文件传输到局域网的另一个系统. 由于文件比较大,耗费的时间可能达到小时级别.这时我们可能就会想能 ...

  8. ROS话题通信中创建自定义数据类型的两种方式

    一.在同一个功能包下创建.msg文件 1.在功能包目录下创建msg文件夹 2.在文件夹中创建.msg文件 3.在该功能包的package.xml文件中加入: <build_depend>m ...

  9. 静态tableview组背景色的更改及自定义cell的两种方式

    为什么80%的码农都做不了架构师?>>>    1 修改默认的静态tableview背景色的方法: UITableView *orderTableView = [[UITableVi ...

  10. java后台实现自定义排序,java 两种方式实现自定义排序

    package test; //Comparable 使对象本身具有可比性,这种方式称为元素的自然顺序或默认顺序 //Comparator 元素自身不具备比较性或者比较性不是所需要的,在集合初始化让其 ...

最新文章

  1. Android的Intent和IntentFilter应用说明一例
  2. 新华社报道关注松果出行:助力国家碳达峰碳中和目标
  3. 自动化运帷之分布式监控 zabbix 使用
  4. 卡尔曼滤波、扩展卡尔曼滤波、无迹卡尔曼滤波以及粒子滤波原理
  5. matlab consumption,Lesage matlab 空间
  6. php5.5 安装pdo oci,PHP5.3连接Oracle客户端及PDO_OCI模块的安装方法
  7. centos安装rabbitmq_SpringCloud之RabbitMQ安装
  8. R语言聚类分析可视化
  9. 文献阅读---对β-三酮除草剂具有广谱抗性的一个水稻基因研究
  10. 协同过滤(collaborative filtering)
  11. R(一)一次R排错的全过程
  12. 手机克隆常见问题 安卓版手机便签怎么导出
  13. 【机器人基础】机器人的运动学控制和动力学控制
  14. 混音 文件 生成 linux,linux 混音
  15. 计算机学生应不应该考研?附上袁哥的考研经历
  16. jQuery知识重构
  17. FastAdmin必坑指南《基础篇》
  18. C# P3色域计算、计算色域覆盖率、已知坐标求多边形面积计算、已知两条线的坐标求交点计算、坐标顺时针排序计算
  19. 程序求1!+2!+3!+...+1000!的和, 求1到1000阶乘之和
  20. Windows Socket 1.1库函数参考大全

热门文章

  1. 【论文阅读】Network In Network
  2. ACL2021 | 一种巧妙解决NER覆盖和不连续问题的方法
  3. 【人才引进】博士补贴75万,硕士补贴20万,这个南方城市,高待遇引才150人!...
  4. 从Word Embedding到Bert模型——自然语言处理预训练技术发展史
  5. matplotlib -plt.rcparams.update
  6. numpy - np.where
  7. numpy-np.random.permutation
  8. 易学性vs易用性,产品应该选哪边?
  9. 35线性映射02—— 线性映射概念与运算、矩阵表示
  10. 机器学习中的数学(二):参数估计与似然函数(MLE)