一、UITabBarController简介

UITabBarController - 选项卡控制器,与导航控制器一样,也被广泛用于各种ios应用程序。顾名思义,选项卡控制器在屏幕底部显示一系列“选显卡”,这些选项卡表示为图标和文本,用户触摸它们将在不同的场景间切换。和UINavigationController类似,UITabBarController也可以用来控制多个页面导航,用户可以在多个视图控制器之间移动,并可以定制屏幕底部的选项卡栏。
借助屏幕底部的选项卡栏,UITabBarController不必像UINavigationController那样以栈的方式推入和推出视图,而是建立一系列的控制器(这些控制器可以是UIViewController、UINavigationController、UITableViewController等)并将它们添加到选项卡栏,使每个选项卡对应一个控制器。每个场景都呈现了应用程序的一项功能,或是提供了一种查看应用程序的独特方式。


二、UITabBarController的原理

  • 当UITabBarController做为Window的根控制器时,程序一启动,UITabBarController就会一次性初始化所有子控制器,但是默认只加载第一个控制器视图,其他视图控制器只初始化,但默认不会加载,只有在需要显示的时候才调用loadView方法加载。特殊情况:在AppDelegate中设置其他的子控制器视图的背景颜色,就会提前加载该控制器视图,但不显示该视图。

  • 每一个控制器视图只加载一次,就会一直存在内存中,当切换子控制器时直接显示,不显示在屏幕上的子控制器不会被销毁。当遇到内存警告时,会释放掉没有加载的子控制器。

  • 每个视图控制器都有一个tabBarController属性,通过它可以访问所在的UITabBarController,而且对于UITabBarController的直接子视图,其tabBarController属性相当于它的父视图parentViewController。

  • 每个视图控制器都有一个tabBarItem属性,通过它控制视图在UITabBarController的tabBar中的显示信息

  • tabBarItem的image属性必须是png格式(建议大小32*32),并且打开alpha通道否则无法正常显示。

  • 当往UITabBarController添加子控制器,标签栏就会有序的自动生成对应的UITabBarButton对象,有多少个子控制器,标签栏就有多少个UITabBarButton对象,但是子控制器的数量超过5个的时候,标签栏上的第五个UITabBarButton对象就会显示成”More”类型的按钮。

其他注意点:

  1. UITabBarController没有根控制器的概念。在添加了相同的子控制器,不会增加tabitem的数量。子控器可以是UIViewController、UINavigationController、UITableViewController或者其他的视图控制器
  2. UITabBarButton在UITabBar中的位置是均分的,UITabBar的高度为49,UITabBarButton⾥面显⽰什么内容,由对应子控制器的tabBarItem属性来决定
  3. UITabBarController一般作为应用程序的rootViewController,但是它不能作为UINavigationController的根控制器
  4. UITabBarController默认只支持竖屏,当设备方向放生变化时候,它会查询viewControllers属性中包含的所有ViewController,仅当所有的viewController都支持该方向时,UITabBarController才会发生旋转,否则默认的竖向
  5. UITabBar继承于UIView,方便用户切换到对应的界面,当往标签控制器里添加子控制器,标签栏就会有序的自动生成对应的标签;创建一个标签控制器,就默认创建一个标签栏,标签栏最多显示5个标签

三、UI结构与源码结构图解


四、基本使用

基本使用流程:
  1. 初始化UITabBarController
  2. 设置UIWindow的rootViewController为UITabBarController
  3. 创建相应的子控制器(viewcontroller)
  4. 把子控制器添加到UITabBarController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-(void)useSystemTabBarControoler{
// 创建窗口
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
//1 创建标签控制器
UITabBarController *tabBarC = [[UITabBarController alloc] init];
//2 设置窗口的根控制器为标签控制器
self.window.rootViewController = tabBarC;
// 显示窗口
[self.window makeKeyAndVisible];

//3 创建相应的子控制器
ViewController *v1 = [[ViewController alloc] init];
v1.tabBarItem.title = @"首页";
v1.tabBarItem.selectedImage = [UIImage imageNamed:@""];
v1.tabBarItem.badgeValue = @"10";

ViewController *v2 = [[ViewController alloc] init];
UINavigationController *nav2 = [[UINavigationController alloc] initWithRootViewController:v2];
nav2.tabBarItem.title = @"我的";
nav2.tabBarItem.selectedImage = [UIImage imageNamed:@""];

//4 把子控制器添加到UITabBarController
tabBarC.viewControllers = @[v1,nav2];

// 设置标签栏的背景图片
tabBarC.tabBar.backgroundImage = [UIImage imageNamed:@"Snip20160825_2"];
}

该方式的弊端:

  • 添加子控制器的代码暴露在了外面

五、自定义使用

以自定义的方式实现UITabBaController

步骤:

  • 新建一个继承自UITabBarController的子类
  • 将这个子类设置为根控制器
  • 在这个子类里面实现添加子控制器的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//在自定义的UITaBBarController子类中:

-(void)addEachControllerWithName:(NSString *)controller
andTitle:(NSString *)title
andDefaultImage:(NSString *)defaultImage
andSelectedImage:(NSString *)selectedImage{

// 添加子控制器
Class class = NSClassFromString(controller);
UIViewController *VC01 = [[class alloc] init];

#pragma mark -- 文字效果设置
// 设置标题
VC01.tabBarItem.title = title;
//文字和图片一样是默认的蓝色,所以也要进行设置才能达到自己想要的效果
//方案一: 通过setTitleTextAttributes属性设置(但是这个方法很麻烦,每次设置的时候都需要写很多代码
// 设置文字属性
NSMutableDictionary * attrs = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[UIFont systemFontOfSize:12.0],NSFontAttributeName,
[UIColor brownColor],NSForegroundColorAttributeName,
nil];
[VC01.tabBarItem setTitleTextAttributes:attrs forState:UIControlStateSelected];

#pragma mark -- 图片设置
// 设置默认图片
VC01.tabBarItem.image = [UIImage imageNamed:defaultImage];

//这种情况下因为有系统默认的蓝色渲染,会导致选中图标selectedImage不是图片原来的样子,可以通过以下两种方式解决
UIImage * image = [UIImage imageNamed:selectedImage];
//方法1 设置渲染模式 - 保持原始的渲染
//方法2:直接在图片文件夹的属性里面进行设置 - 将render as设置成original image
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
// 设置选中图片
VC01.tabBarItem.selectedImage =image;

VC01.view.backgroundColor = [UIColor yellowColor];

[self addChildViewController:VC01];
}
  • 由于文字属性每次设置setTitleTextAttributes的时候代码很多很烦,所以要进行简化
  • 当方法方法后面有UI_APPERANCE宏的时候,就可以通过appearance对象一次性设置

步骤

  • 拿到那个item的apperance
  • 对这个item进行设置、
  • 之后所有的item都会是这个属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//在自定义的UITabBarController子类中

//当方法方法后面有UI_APPERANCE宏的时候,就可以通过appearance对象一次性设置
//- (void)setTitleTextAttributes:(nullable NSDictionary<NSString *,id> *)attributes forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;
-(void)settingTitleColorByAppearance{
// 通过appearance统一设置UITabbarItem的文字属性
NSMutableDictionary * attrs = [NSMutableDictionary dictionary];
attrs[NSFontAttributeName] = [UIFont systemFontOfSize:12.0]; // 设置文字大小
attrs[NSForegroundColorAttributeName] = [UIColor grayColor]; // 设置文字的前景色

NSMutableDictionary * selectedAttrs = [NSMutableDictionary dictionary];
selectedAttrs[NSFontAttributeName] = [UIFont systemFontOfSize:15.0];
selectedAttrs[NSForegroundColorAttributeName] = [UIColor redColor];

UITabBarItem * item = [UITabBarItem appearance]; // 设置appearance
[item setTitleTextAttributes:attrs forState:UIControlStateNormal];
[item setTitleTextAttributes:selectedAttrs forState:UIControlStateSelected];
}


自定义底部tabbar(重点部分)
覆盖UITabBarController自带的tabBar为自定义的tabBar操作原理
  • tabBar上的按钮是在UITabBarController的viewDidAppear的时候拿到 self.tabBar 再调用addSubViews添加上去的;
  • 在viewDidAppear之前把控制器的tabBar换成我们自己的tabBar,就可以把tabBar上的按钮添加到自己的tabBar上;
  • 但是tabBar控制器的tabBar属性是只读的,不能直接赋值,只能通过KVC模式跟换tabbar

步骤

  • 新建一个继承自UITabbar的类
  • 在这个类里面实现初始化
  • layoutSubviews方法里面重新布局
  • 在TabbarController里面跟换tabbar
  • 虽然我们在TabbarController里面换成了自定义的tabbar,但是因为这个tabbar继承自uitabbar,所以它原来的属性和内容还在
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#import "YLCustomTabBar.h"

@interface YLCustomTabBar()

/** 发布按钮 */
@property (nonatomic, weak) UIButton * publishBtn;

@end


@implementation YLCustomTabBar

/**
* 初始化
*/
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {

// 设置tabbar的子控件
UIButton * publishBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[publishBtn setBackgroundImage:[UIImage imageNamed:@"Add_Center"] forState:UIControlStateNormal];
[publishBtn setBackgroundImage:[UIImage imageNamed:@"Add_Center"] forState:UIControlStateHighlighted];
[publishBtn sizeToFit];

[self addSubview:publishBtn];
self.publishBtn = publishBtn;
}
return self;
}

/**
* 重写布局子控件的方法进行布局
*/
- (void)layoutSubviews{
[super layoutSubviews];
NSLog(@"重新布局");


// 1.设置加号按钮的位置
CGPoint temp =self.publishBtn.center;
temp.x=self.frame.size.width/2;
temp.y=self.frame.size.height/2;
self.publishBtn.center=temp;

// 2.设置其它UITabBarButton的位置和尺寸
CGFloat tabbarButtonW =self.frame.size.width /3;
CGFloat tabbarButtonIndex =0;
for (UIView *child in self.subviews) {
Class class = NSClassFromString(@"UITabBarButton");
if ([child isKindOfClass:class]) {
// 设置宽度
CGRect temp1=child.frame;
temp1.size.width=tabbarButtonW;
temp1.origin.x=tabbarButtonIndex * tabbarButtonW;
child.frame=temp1;
// 增加索引
tabbarButtonIndex++;
if (tabbarButtonIndex ==1) {//如果底部有5个按钮这里就是2
tabbarButtonIndex++;
}
NSLog(@"frame----%f",child.frame.origin.x);
}
}
}
@end

//在自定义的UITaBBarController子类中:
[self setValue:[[YLCustomTabBar alloc]init] forKeyPath:@"tabBar"];

![Demo地址:https://github.com/yuanliangYL/iOSUITabBarControllerDeepUsing]