自定义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 = ;
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 = ; 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的两种方式的更多相关文章
- iOS 自定义layer的两种方式
在iOS中,你能看得见摸得着的东西基本都是UIView,比如一个按钮,一个标签,一个文本输入框,这些都是UIView: 其实UIView之所以能显示在屏幕上,完全是因为它内部的一个图层 在创建UIVi ...
- EntityFramework Core 2.0自定义标量函数两种方式
前言 上一节我们讲完原始查询如何防止SQL注入问题同时并提供了几种方式.本节我们继续来讲讲EF Core 2.0中的新特性自定义标量函数. 自定义标量函数两种方式 在EF Core 2.0中我们可以将 ...
- UITabBar背景、icon图标颜色、被选中背景设置以及隐藏UITabBar的两种方式
一.对UITabBar背景和icon图标的一些设置 (1)因为直接给UITabBar设置的背景颜色显示的不纯,半透明的感觉,所以,有时候我们可以直接利用纯色的图片作为背景达到想要的效果: (2)给ic ...
- 【iOS开发-31】UITabBar背景、icon图标颜色、被选中背景设置以及隐藏UITabBar的两种方式
一.对UITabBar背景和icon图标的一些设置 (1)由于直接给UITabBar设置的背景颜色显示的不纯.半透明的感觉,所以,有时候我们能够直接利用纯色的图片作为背景达到想要的效果. (2)给ic ...
- SpringBoot自定义过滤器的两种方式及过滤器执行顺序
第一种 @WebFilter + @ServletComponentScan 注解 1.首先自定义过滤器 如下自定义过滤器 ReqResFilter 必须实现 javax.servlet.Filte ...
- AntDesign VUE:上传组件自定义限制的两种方式(Boolean、Promise)
AntD上传组件 AntDesign VUE文档 第一种方式 beforeUpload(file) { let isLt = true if (filesSize) { isLt = file.siz ...
- android 自定义radiogroup的两种方式
这里先备注下 listview+radiobutton实现 浅显易懂 http://www.haolizi.net/example/view_3312.html 在radiogoup原生态源码的基础 ...
- Hibernate查询返回自定义VO的两种方式
说明:createQuery用的hql语句进行查询,createSQLQuery用sql语句查询: 前者以hibernate生成的Bean为对象装入list返回:后者则是以对象数组进行存储: 一.通过 ...
- egg.js 通过 form 和 ajax 两种方式上传文件并自定义目录和文件名
egg.js 通过 form 和 ajax 两种方式上传文件并自定义目录和文件名 评论:10 · 阅读:8437· 喜欢:0 一.需求 二.CSRF 校验 三.通过 form 表单上传文件 四.通过 ...
随机推荐
- 转:Git 求生手册 - 第三章分支工作
from:http://newbranch.cn/zhi-zuo-fen-zhi-lai-gong-zuo-git-gh-pages-branching/ 来自:片段 实战 说了这么一大堆分支的东西. ...
- tyvj[1089]smrtfun
描述 现有N个物品,第i个物品有两个属性A_i和B_i.在其中选取若干个物品,使得sum{A_i + B_i}最大,同时sum{A_i},sum{B_i}均非负(sum{}表示求和). 输入格式 ...
- CSS IE6/7/8, Firefox, Safari, Chrome, Opera Hack使用简要归纳(转)
网上有很多关于IE6/7/8, Firefox, Safari, Chrome, Opera CSS Hack的文章,但我觉得太过繁杂,要不给出一张看也看不懂的CSS Hack归纳表,要不就是给出一大 ...
- dotnetGen 系列终于开源了
https://github.com/2881099/dotnetGen .Net 3.0 + SqlServer 生成器 https://github.com/2881099/dotnetGen_s ...
- CentOS上搭建Nginx + Mono 运行 asp.net
安装步骤: 一.获取开源相关程序: 1.利用CentOS Linux系统自带的yum命令安装.升级所需的程序库: sudo -sLANG=Cyum -y install gcc gcc-c++ aut ...
- Html5 Egret游戏开发 成语大挑战(二)干净的eui项目和资源准备
现在我们使用egret来起步开发一个名叫<成语大挑战>的小游戏,关于egret的开发环境就不在这里啰嗦了,直接去官方下载安装就可,egret是我见过开发环境部署最简单的解决方案,这个系列教 ...
- CodeGenerator.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace CiCe ...
- IE8和W3C标准下IFRAME刷新和URL的区别
一个页面中包含了一个iframe,我们要刷新这个iframe的情况 url在IE8和W3C以及IE11的区别如下: URL使用相对路径,绝对路径比如http://localhost:5568/替换成I ...
- OS存储器管理(三) 虚拟存储器
基本概念与实现 1)局部性原理 在一段时间内,运行的作业程序仅访问(涉及到)一部分作业代码,即不会涉及整个地址空间.即在一段时间间隔内,仅装入一部分代码,作业照样能正常运行 2)虚拟存储器的引入 作业 ...
- [BZOJ3714][PA2014]Kuglarz(MST)
题目: Description 魔术师的桌子上有n个杯子排成一行,编号为1,2,…,n,其中某些杯子底下藏有一个小球,如果你准确地猜出是哪些杯子,你就可以获得奖品.花费c_ij元,魔术师就会告诉你杯子 ...