iOS中UITabBarController的剖析

该博客详细探讨了Apple如何构建UITabBarController,并指导读者如何进行自定义实现,提供了深入的理解和实践建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文对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就变为了我们希望看到的效果:












评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值