您的位置:首页 > 产品设计 > UI/UE

[置顶] iOS中UITabBarController的剖析

2015-10-14 21:28 691 查看

本文对UITabBarController进行分析,研究苹果内部是怎么构造出这个类的,以及我们怎么去自定义一个类

1、内部实现细节分析
我们尝试去打印tabBar,看看一个没有进行操作的UITarBarController是怎么样的:
UITabBarController *tabBarController = [[UITabBarController alloc]init];
NSLog(@"%@",tabBarController.tabBar.subviews);
结果发现打印出来是这样的:
(
)
(>_<) 是空的,不过没关系,我们可以通过写一个继承于UITabBarController的类,来查看内部细节

新建一个继承于UITabBarController类的类NTTabBarController,然后在viewDidLoad中为self添加4个子控制器接着在viewDidAppear中打印这样一句代码(在viewDidLoad中打印是没有结果的)
- (void)viewDidAppear:(BOOL)animated
{
NTLog(@"%@",self.tabBar.subviews);
}
打印出来的结果是这样:
(
"<_UITabBarBackgroundView: 0x7fcd927a1b40; frame = (0 0; 320 49); autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x7fcd927070c0>>",
"<UITabBarButton: 0x7fcd92453690; frame = (2 1; 76 48); opaque = NO; layer = <CALayer: 0x7fcd92454640>>",
"<UITabBarButton: 0x7fcd9279d810; frame = (82 1; 76 48); opaque = NO; layer = <CALayer: 0x7fcd92426010>>",
"<UITabBarButton: 0x7fcd92458a80; frame = (162 1; 76 48); opaque = NO; layer = <CALayer: 0x7fcd92459450>>",
"<UITabBarButton: 0x7fcd9245c4b0; frame = (242 1; 76 48); opaque = NO; layer = <CALayer: 0x7fcd92426570>>",
"<UIImageView: 0x7fcd9245db40; frame = (0 -0.5; 320 0.5); autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x7fcd92426de0>>"
)
在分析之前,需要明确几个参数值,导航栏的高度是49,iphone5之前屏幕宽度都是320,iphone6的屏幕宽度是375,iphone6 plus的屏幕宽度是414。
(1)第一个是UITabBarBackgroundView类,我们好像从来都没有使用过这个类,接着去试着敲出这个类,发现没有提示,那么就说明这个类是私有的,苹果不希望外部去访问和操作它。不过通过这个类的命名以及frame来看,这个类是tarBar中于背景相关的。
(2)最后一个是UIImageView类,这个对于大家当然是很熟悉的,通过看它的frame,它的宽度是320,然后高度仅仅为0.5,那么我们联想到的是tabBar中的最上面的分割线。
(3)中间有4个UITabBarButton类的对象,这个类也是私有的,通过查看frame,可以知道苹果所设计的tabBar中都是宽76,高48这样的尺寸的。

了解完了内部结构之后,进一步深入到tarBar中去讨论:新建一个继承于UITabBar的类NTTabBar,同时在以上所使用的NTTabBarController中去使用NTTabBar而不是系统所给的UITabBar,导入NTTabBar类,我们尝试去赋值self.tarBar:
self.tabBar = [[NTTabBar alloc]init];
但是xcode报错,错误为:
Assignment to readonly property --->试图修改只读属性,按住command然后点击tarBar,在文章中发现:
@property(nonatomic,readonly) UITabBar *tabBar
那么这时候我们可以使用KVC进行修改,无论属性是通过什么来修饰的,都是可以使用KVC进行赋值修改的:
[self setValue:[[NTTabBar alloc]init] forKeyPath:@"tabBar"];
这时候就可以在我们自定义的NTTabBar类中进行详细操作:
我们知道对于一个控件中子控件的排布,都是写在了layoutSubViews里面。我们在自定义的NTTabBar中去重写layoutSubViews方法:
- (void)layoutSubviews
{
NSLog(@"%@",self.subviews);
}
这样打印的结果为:
(
"<UITabBarButton: 0x7fe5b9c3ed00; frame = (2 1; 76 48); opaque = NO; layer = <CALayer: 0x7fe5b9c3fcb0>>",
"<UITabBarButton: 0x7fe5b9f9eaa0; frame = (82 1; 76 48); opaque = NO; layer = <CALayer: 0x7fe5b9f9afc0>>",
"<UITabBarButton: 0x7fe5b9fa92a0; frame = (162 1; 76 48); opaque = NO; layer = <CALayer: 0x7fe5b9f9e710>>",
"<UITabBarButton: 0x7fe5b9e6dbf0; frame = (242 1; 76 48); opaque = NO; layer = <CALayer: 0x7fe5b9e6e0a0>>"
)
可以发现,这里只有我们添加到4个子控制器对应的UITabBarButton,而没有那个作为背景的UITabBarBackgroundView,和作为分隔线的UIImageView。
我们又尝试修改:
- (void)layoutSubviews
{
[super layoutSubviews];
NSLog(@"%@",self.subviews);
}
这样打印出来的结果为:
(
"<_UITabBarBackgroundView: 0x7fa952c17ab0; frame = (0 0; 320 49); autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x7fa952c7f430>>",
"<UITabBarButton: 0x7fa952d52d30; frame = (2 1; 76 48); opaque = NO; layer = <CALayer: 0x7fa952d53d20>>",
"<UITabBarButton: 0x7fa952ccd770; frame = (82 1; 76 48); opaque = NO; layer = <CALayer: 0x7fa952ccd5d0>>",
"<UITabBarButton: 0x7fa952e3ea80; frame = (162 1; 76 48); opaque = NO; layer = <CALayer: 0x7fa952e3f370>>",
"<UITabBarButton: 0x7fa952e42370; frame = (242 1; 76 48); opaque = NO; layer = <CALayer: 0x7fa952e3e970>>",
"<UIImageView: 0x7fa952e4af60; frame = (0 -0.5; 320 0.5); autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x7fa952e36650>>"
)
这时候那两个控件就出来了,这是因为调用了super的方法。

2、重写构造tarBar内部结构
首先需要明确的是,对于UIView类或者是UIView类的子类,我们都需要在layoutSubViews里面去控制,所以我们在layoutSubViews里面进行重新布局(本例是模仿新浪微博的tarBar):
#import "NTTabBar.h"

@interface NTTabBar()
@property (nonatomic , strong) UIButton *middleButton;
@end

@implementation NTTabBar

// 懒加载
- (UIButton *)middleButton
{
if (_middleButton == nil) {
_middleButton = [UIButton buttonWithType:UIButtonTypeCustom];
[_middleButton setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button"] forState:UIControlStateNormal];
[_middleButton setBackgroundImage:[UIImage imageNamed:@"tabbar_compose_button_highlighted"] forState:UIControlStateHighlighted];
[_middleButton setImage:[UIImage imageNamed:@"tabbar_compose_icon_add" ] forState:UIControlStateNormal];
[_middleButton setImage:[UIImage imageNamed:@"tabbar_compose_icon_add_highlighted"] forState:UIControlStateHighlighted];
}
return _middleButton;
}

- (void)layoutSubviews
{
// 调用super的方法为了创建UIImageView的分割线,和UITabBarBackgroundView的背景控件
[super layoutSubviews];

int index = 0;
for (UIView *view in self.subviews) {
// 通过判断class的类型来过滤掉背景控件和分割线控件
if ([view isKindOfClass:NSClassFromString(@"UITabBarButton")]) {
// 设置view的宽度
view.width = 320 / 5;
// 设置view的x--->由index来控制
view.x = index * 320/5;
// 再第三个位子留一个空位给自定义的middleButton
if(index == 1){
index++;
// 创建middleButton并添加到tarBar
self.middleButton.height = 48;
self.middleButton.width = 320/5;
self.middleButton.x = index * 320/5;
self.middleButton.y = 2;
[self addSubview:self.middleButton];
}
index++;
}
}
}
@end
通过在layoutSubViews方法中对进行操作,使得tarBar就变为了我们希望看到的效果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: