从系统UITabbar到自定义UITabbar

自述

技术菜鸟第一篇博文,目的是自我总结,自省。一直有写博客的打算,一直未跨出这步,就此作为闭眼摸出的第一步。
到目前为止,我所参加的App项目都是基于UITabbarController上的多层页面展示。之前都是在别人搭建好的框架上实现部分功能,或用系统自带的UITabbarController做简单的应用。这次有幸从0开始,经过摸索做出完整的app,需要总结的地方颇多。

自定义Tabbar的基本实现过程

  1. 首先需要隐藏系统自带的tabbar;
  2. 然后创建一个自定义的tabbar(一个UIView ,给它命名为MyTabbar) 并添加到系统tabbar这个view上;
  3. 接着只需要在MyTabbar上添加按钮来实现自定义Tabbar的绘制。
  4. 最后完成按钮点击来响应子Controller之间的跳转。

在Xcode上详细完成过程及详解

首先打开Xcode 并创建一个新的Single View Application。创建一个BaseTabbarController类,继承UITabBarController 作为程序启动后的rootViewController。
Xcode创建项目时默认运行Main.storyboard及载入与之相关联的ViewController类(可以在Supporting Files文件夹下info.plist中删除Main storyboard file base name 这项,便不会自动加载了,然后将Main.storyboard文件删掉)。
接下来将BaseTabbarController设置为app的rootViewController. 在AppDelegate.m文件中加入下列代码:

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    BaseTabbarViewController *tab = [[BaseTabbarViewController alloc]init];
    self.window.rootViewController = tab;
    [self.window makeKeyAndVisible];

    return YES;
}

Tabbar上假设有四个选项。还需创建三个类(ViewController2,ViewController3,ViewController4)继承UIViewController。分别将四个ViewController的背景颜色设置不同。
如:把ViewController 背景设为蓝色,在ViewController.m中加入代码

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor blueColor];
}
先看看系统自带Tabbar的效果:

在BaseTabbarViewController.m中先创建 initSystemTabbar()方法,然后再viewDidLoad()方法中 调用。
创建此方法代码为(此处为系统默认图标和title):

- (void)initSystemTabbar
{
    ViewController *vc1 = [[ViewController alloc]init];
    vc1.tabBarItem = [[UITabBarItem alloc]initWithTabBarSystemItem:UITabBarSystemItemBookmarks tag:1];

    ViewController2 *vc2 = [[ViewController2 alloc]init];
    vc2.tabBarItem = [[UITabBarItem alloc]initWithTabBarSystemItem:UITabBarSystemItemFavorites tag:1];

    ViewController3 *vc3 = [[ViewController3 alloc]init];
    vc3.tabBarItem = [[UITabBarItem alloc]initWithTabBarSystemItem:UITabBarSystemItemDownloads tag:1];

    ViewController4 *vc4 = [[ViewController4 alloc]init];
    vc4.tabBarItem = [[UITabBarItem alloc]initWithTabBarSystemItem:UITabBarSystemItemHistory tag:1];
    //在tabbar上面显示数组内的根view。
    [self setViewControllers:@[vc1,vc2,vc3,vc4]];
}

然后运行就能看到最简单的tabbar效果了。
系统tabbar




当然,大多数app都是用的自己的图标和title。
先随便找四个图标png图标放入项目中Images.xcassets文件夹。然后把上面initSystemTabbar方法稍作改变。就能生成自己想要的标题和图案了,方法如下:

    ViewController *vc1 = [[ViewController alloc]init];
    //设置tabbar item的标题 图片 和 选中图片。
    vc1.tabBarItem = [[UITabBarItem alloc]initWithTitle:@"主页" image:[UIImage imageNamed:@"TabbarAssortment"] selectedImage:[UIImage imageNamed:@"TabbarAssortment"]];

    ViewController2 *vc2 = [[ViewController2 alloc]init];
    vc2.tabBarItem = [[UITabBarItem alloc]initWithTitle:@"消息" image:[UIImage imageNamed:@"TabbarActivities"] selectedImage:[UIImage imageNamed:@"TabbarActivities"]];

    ViewController3 *vc3 = [[ViewController3 alloc]init];
    vc3.tabBarItem = [[UITabBarItem alloc]initWithTitle:@"发现" image:[UIImage imageNamed:@"Tabbarcar"] selectedImage:[UIImage imageNamed:@"Tabbarcar"]];

    ViewController4 *vc4 = [[ViewController4 alloc]init];
    vc4.tabBarItem = [[UITabBarItem alloc]initWithTitle:@"我" image:[UIImage imageNamed:@"TabbarHome"] selectedImage:[UIImage imageNamed:@"TabbarHome"]];

    [self setViewControllers:@[vc1,vc2,vc3,vc4]];//在tabbar上面显示数组内的根view

运行后可看到效果:
自定义标题和按钮图片



自定义tabbar的效果

系统tabbar的限制在于 高度不能自己设置,图片是灰色的,最多显示5个,多的则会出现more,等。。。
首先要做的事隐藏System的TabbarItem,先看系统Tabbar上的子视图是属于什么类型的。
加入隐藏Tabbar的方法:

- (void)hideSystemTabbar
{
    for (UIView *child in self.tabBar.subviews) {
        NSLog(@"%@",[child superclass]);
    }
}

运行,可以看到 console区输出四个UIControl,由此可以判定Tabbar上的四个按钮是UIControl的子类,所以可以通过自省把他们移除。
改变代码如下:

- (void)hideSystemTabbar
{
    for (UIView *child in self.tabBar.subviews) {
        NSLog(@"%@",[child superclass]);
         if ([child isKindOfClass:[UIControl class]]) {
            [child removeFromSuperview];
        }
    }
}

在 viewWillAppear中调用此方法。
运行,并能看到屏幕下方只剩下空白的Tabbar了。

完成了第一部分,接下来创建一个自己的Tabbar(命名为customTabbar) 来实现原tabbar的功能。
新建一个CustomTabbar类继承UIView。
在BaseTabbarViewController.m中加入私有的属性:

@interface BaseTabbarViewController()

@property (nonatomic,strong)CustomTabbar *customTabbar;

@end

将之前的initSystemTabbar的调用注释掉。加入initCustomTabbar方法。

- (void)initCustomTabbar
{
    self.customTabbar = [[CustomTabbar alloc]initWithFrame:self.tabBar.bounds];
    self.customTabbar.backgroundColor = [UIColor greenColor];
    [self.tabBar addSubview:self.customTabbar];
}

此时再运行便可以得到一个绿色背景的Tabbar了(bounds属性的意思是UIView在自身坐标系下的frame,更通俗点就是(0,0,自己的宽,自己的高)。此时所定义的customTabbar跟原先系统的frame一样。接下来需要做的便是Draw CustomTabbar。

作为一个Tabbar一般的需求是显示 标题、未选中时的图标、选中时的图标,而且可以点击响应,很容易的想到用UIButton来能胜任此工作。此时CustomTabbar上的item数量应该是BaseTabbarController里能动态控制的,所以提供一个公共方法供Controller调用。在CustomTabbar.h中提供方法- (void)addTabBarButtonWithItem:(UITabBarItem *)item(因为UITabBarItem自带上述三个需要显示的属性,所以来作为传递介质比较方便)。在CustomTabbar.m中实现如下:

- (void)addTabBarButtonWithItem:(UITabBarItem *)item
{
    UIButton *btn = [[UIButton alloc]init];
    [btn setTitle:item.title forState:UIControlStateNormal];
    [btn setImage:item.image forState:UIControlStateNormal];
    [btn setImage:item.selectedImage forState:UIControlStateSelected];

    [self addSubview:btn];
}

考虑到每在tabbar上添加一个Button便要改变所有Button的frame,故可以重写UIView的layoutSubviews方法(layoutSubviews方法不能直接调用,只能重写 然后会在下列时候被调用1、init初始化不会触发layoutSubviews;2、addSubview会触发layoutSubviews;3、设置view的Frame会触发layoutSubviews;当然前提是frame的值设置前后发生了变化;4、滚动一个UIScrollView会触发layoutSubviews;5、旋转Screen会触发父UIView上的layoutSubviews事件;6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件):

- (void)layoutSubviews
{
    //设置tabbar上Button的宽高
    CGFloat btnWidth = self.frame.size.width/self.subviews.count;
    CGFloat btnHight = self.frame.size.height;

    for (int i = 0; i < self.subviews.count; i ++) {
        UIButton *btn = self.subviews[i];
        btn.frame = CGRectMake(i * btnWidth, 0, btnWidth, btnHight);
        btn.tag = i;
    }
}

CustomTabbar.h 中声明此方法并添加一个selectedButton属性记录当前选中Button:

@interface CustomTabbar : UIView

@property (nonatomic ,strong) UIButton *selectedButton;

- (void)addTabBarButtonWithItem:(UITabBarItem *)item;
@end



然后回到BaseTabbarController,利用CustomTabbar控件提供的接口根据需求自定义Tabbar。传递所用的参数是UITabBarItem类型。可用BaseTabbarController的根viewcontroller的tabbbarItem作为传递参数。

- (void)initChildViewController
{
    ViewController *vc1 = [[ViewController alloc]init];
    [self setUpChildController:vc1 title:@"主页" imageName:@"TabbarAssortment" selectedImageName:@"TabbarAssortment"];

    ViewController2 *vc2 = [[ViewController2 alloc]init];
    [self setUpChildController:vc2 title:@"消息" imageName:@"TabbarActivities" selectedImageName:@"TabbarActivities"];

    ViewController3 *vc3 = [[ViewController3 alloc]init];
    [self setUpChildController:vc3 title:@"发现" imageName:@"Tabbarcar" selectedImageName:@"Tabbarcar"];

    ViewController4 *vc4 = [[ViewController4 alloc]init];
    [self setUpChildController:vc4 title:@"我" imageName:@"TabbarHome" selectedImageName:@"TabbarHome"];
}

此时再运行就能看到自己定义的绿色背景的CustomTabbar了。
自定义Tabbar


大体外形出来了,现在还有两点需要完善,1.CustomBar上button的图标和标题是左右排列的需要改成上下。2.button点击时背景的改变和点击时页面的跳转。都与Button相关,将Button提取出来单独写。
新建类CustomTabbarButton 继承UIButton。需要做的是重新设置button上imageView和TitleLabel的frame,通过button的frame来计算两个subview的frame。图片占整个高度的60%,标题占40%。在CustomTabbarButton.m中构建:

#define imagePercent 0.6
- (CGRect)imageRectForContentRect:(CGRect)contentRect{
    return CGRectMake(0, 0, contentRect.size.width, contentRect.size.height * imagePercent);
}
- (CGRect)titleRectForContentRect:(CGRect)contentRect{
    CGFloat titleY = contentRect.size.height * imagePercent;
    return CGRectMake(0, titleY, contentRect.size.width, contentRect.size.height - titleY);
}

然后在CustomTabbarButton的init方法中为subview布局:

- (instanceType)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.imageView.frame = [self imageRectForContentRect:frame];
        //imageview居中 防止拉伸
        self.imageView.contentMode = UIViewContentModeCenter;
        self.titleLabel.frame = [self titleRectForContentRect:frame];
        self.titleLabel.textAlignment = NSTextAlignmentCenter;
        self.titleLabel.font = [UIFont systemFontOfSize:15];
    }
    return self;
}

把CustomTabbar.m中的UIButton全部置换成CustomTabbarButton,再运行并能看到上下结构的图标及标题了。
最后需要完成的是点击按钮时的页面转换及状态按钮状态变化。按钮状态变化部分由控件自己控制,而页面跳转问题需要BaseTabbarController来控制跳转。考虑到Tabbar的点击按钮事件需要通知到TabbarController所以当前能想到两种比较合适的方式来实现,block和代理。 这边我选择用代理来完成。
在CustomTabbar.h中声明代理:


@class CustomTabbar;

@protocol CustomTabBarDelegate <NSObject>

- (void)tabBar:(CustomTabbar *)tabBar didSelectedButtonFrom:(NSInteger)from to:(NSInteger)to;

@end

并在.h中添加属性:

 @property (nonatomic, weak) id<CustomTabBarDelegate> delegate;

然后在.m中通知代理。先给之前tabbar上的button添加点击事件(在addTabBarButtonWithItem:(UITabBarItem )item* 方法中):

    [btn addTarget:self action:@selector(onClickButton:) forControlEvents:UIControlEventTouchUpInside];

然后添加onClickButton方法:

- (void)onClickButton:(CustomTabBarButton *)button {
    // 通知代理
    if ([self.delegate respondsToSelector:@selector(tabBar:didSelectedButtonFrom:to:)]) {
        [self.delegate tabBar:self didSelectedButtonFrom:self.selectedButton.tag to:button.tag];
    }

    self.selectedButton.selected = NO;
    self.selectedButton.backgroundColor = [UIColor grayColor];

    button.selected = YES;
    self.selectedButton = button;
    self.selectedButton.backgroundColor = [UIColor whiteColor];
}

最后在BaseTabbarController中响应代理。首先在

- (void)onClickButton:(CustomTabBarButton *)button {
    // 通知代理
    if ([self.delegate respondsToSelector:@selector(tabBar:didSelectedButtonFrom:to:)]) {
        [self.delegate tabBar:self didSelectedButtonFrom:self.selectedButton.tag to:button.tag];
    }

    self.selectedButton.selected = NO;
    self.selectedButton.backgroundColor = [UIColor grayColor];

    button.selected = YES;
    self.selectedButton = button;
    self.selectedButton.backgroundColor = [UIColor whiteColor];
}

最后在BaseTabbarController中响应代理CustomTabBarDelegate。然后将:

        self.customTabbar.delegate  = self;

最后实现代理方法:

- (void)tabBar:(CustomTabbar *)tabBar didSelectedButtonFrom:(NSInteger)from to:(NSInteger)to
{
    self.selectedIndex = to;
}

再运行并能看到自定义的效果了。 大体流程就是这样。

废话较多,over。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值