iOS 关于tabBar的几处笔记

avatar
奇舞团移动端团队 @奇舞团

级别: ★☆☆☆☆
标签:「iOS」「状态栏」「导航栏」
作者: dac_1033
审校: QiShare团队


1. 创建一个带tabBar的App

一般项目中的App界面框架结构,如下:App界面框架结构

本例中创建了一个QiTabBarController继承于UITabBarController,并作为window的rootViewController,则在QiTabBarController中写以下代码即可实现上面所述结构。

//// AppDelegate.m 中代码
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    QiTabBarController *tabBarController = [[QiTabBarController alloc] init];
    _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    [_window setBackgroundColor:[UIColor whiteColor]];
    [_window setRootViewController:tabBarController];
    [_window makeKeyAndVisible];
    
    return YES;
}


//// QiTabBarController.m 中代码
#import "QiTabBarController.h"
#import "QiNavigationController.h"
#import "FirstController.h"
#import "SecondController.h"

@interface QiTabBarController ()

@end

@implementation QiTabBarController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setupChildControllers];
}

- (void)setupChildControllers {
    
    FirstController *first = [[FirstController alloc] init];
    QiNavigationController *firstNav = [[QiNavigationController alloc] initWithRootViewController:first];
    firstNav.tabBarItem.title = @"FirTab";
    firstNav.tabBarItem.image = [[UIImage imageNamed:@"tab_team_normal"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    firstNav.tabBarItem.selectedImage = [[UIImage imageNamed:@"tab_team_50"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    
    SecondController *second = [[SecondController alloc] init];
    QiNavigationController *secondNav = [[QiNavigationController alloc] initWithRootViewController:second];
    secondNav.tabBarItem.title = @"SecTab";
    secondNav.tabBarItem.image = [[UIImage imageNamed:@"tab_mine_normal"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    secondNav.tabBarItem.selectedImage = [[UIImage imageNamed:@"tab_mine_50"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    
    self.viewControllers = @[firstNav, secondNav, thirdNav, fourthNav, fifthNav, sixthNav];
}

@end

如果App设计有底部标签栏(UITabBar),我们可以通过Xcode的调试功能“Debug View Hierarchy”看到UITabBar的结构。

tabBar中子view的层次结构

当tabBar中的UITabItem个数超过5个时,tabBar右侧会出现一个more按钮,点击more按钮进入一个名为more的controller,点击展示出来的其他tabItem即可可进入相应的controller。

tabBar中的“更多” 其中,名为more的controller导航栏右侧有edit按钮,点击进入后,可拖动编辑tabBar中所示的tab顺序。在拖动编辑tabBar中所示的tab顺序时,系统会自动调用UITabBarDelegate中的相应方法(如果需要,可在QiTabBarController中直接实现UITabBarDelegate协议方法):

- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item; // called when a new view is selected by the user (but not programatically)

/* called when user shows or dismisses customize sheet. you can use the 'willEnd' to set up what appears underneath. 
 changed is YES if there was some change to which items are visible or which order they appear. If selectedItem is no longer visible, 
 it will be set to nil.
 */

- (void)tabBar:(UITabBar *)tabBar willBeginCustomizingItems:(NSArray<UITabBarItem *> *)items __TVOS_PROHIBITED;                     // called before customize sheet is shown. items is current item list
- (void)tabBar:(UITabBar *)tabBar didBeginCustomizingItems:(NSArray<UITabBarItem *> *)items __TVOS_PROHIBITED;                      // called after customize sheet is shown. items is current item list
- (void)tabBar:(UITabBar *)tabBar willEndCustomizingItems:(NSArray<UITabBarItem *> *)items changed:(BOOL)changed __TVOS_PROHIBITED; // called before customize sheet is hidden. items is new item list
- (void)tabBar:(UITabBar *)tabBar didEndCustomizingItems:(NSArray<UITabBarItem *> *)items changed:(BOOL)changed __TVOS_PROHIBITED;  // called after customize sheet is hidden. items is new item list

2. 设置tabBar的样式

  • 设置tabBar的基本样式 在普通的项目中,一般只需要修改tabBar中每个item展示的图片icon和title及item的位置,或item中icon与title的间隔。
- (void)setTabBarStyle {
    
    //去掉TabBar顶部的线
    UITabBar *tabBar = self.tabBar;
    [tabBar setShadowImage:[UIImage new]];
    [tabBar setBackgroundImage:[UIImage new]];
    tabBar.translucent = NO;

    for (UITabBarItem *item in self.tabBar.items) {
        // 设置UITabBarItem中title样式
        [item setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor lightGrayColor]} forState:UIControlStateNormal];
        [item setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor darkGrayColor]} forState:UIControlStateSelected];

        // 设置UITabBarItem中按钮大小,及img与title间距
        [item setImageInsets:UIEdgeInsetsMake(-5, 0, 5, 0)];
    }
}
  • 自定义tabBar按钮动画 一般常见的tabBar按钮动画有两种:点击时icon缩小放大动画,icon像.gif型图片一样的动画。 tabBar按钮动画

需求看起来很简单,我们应该已经有了思路,首先,我们应该找到这个当前我们要操作的这个tabBarBtn, 在QiTabBarController中实现UITabBarControllerDelegate方法,其中可以监听到tabBar上的点击动作:

- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
    
    NSInteger index = [tabBarController.childViewControllers indexOfObject:viewController];
    
    UIButton *tabBarBtn = tabBarController.tabBar.subviews[index+1];
    UIImageView *imageView = tabBarBtn.subviews.firstObject;
    
    // 我们把动画加到这个imageView上,即可实现动画效果

    // ......

    return YES;
}

在本文实例中,我们只设置了两个tab项,第一个tabBarBtn使用.gif型动画,第二个tabBarBtn使用缩小放大动画,整个QiTabBarController实现代码如下:

#import "QiTabBarController.h"
#import "QiNavigationController.h"
#import "FirstController.h"
#import "SecondController.h"

@interface QiTabBarController () <UITabBarControllerDelegate>

@property (strong, nonatomic) NSMutableArray<UIImage *> *imgArr;

@end

@implementation QiTabBarController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self setupChildControllers];
    [self setTabBarStyle];
    [self initImages];
}

- (void)setupChildControllers {
    
    self.view.backgroundColor = [UIColor whiteColor];
    self.delegate = self;
    
    FirstController *first = [[FirstController alloc] init];
    QiNavigationController *firstNav = [[QiNavigationController alloc] initWithRootViewController:first];
    firstNav.tabBarItem.title = @"FirTab";
    firstNav.tabBarItem.image = [[UIImage imageNamed:@"tab_team_normal"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    firstNav.tabBarItem.selectedImage = [[UIImage imageNamed:@"tab_team_50"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    
    SecondController *second = [[SecondController alloc] init];
    QiNavigationController *secondNav = [[QiNavigationController alloc] initWithRootViewController:second];
    secondNav.tabBarItem.title = @"SecTab";
    secondNav.tabBarItem.image = [[UIImage imageNamed:@"tab_mine_normal"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    secondNav.tabBarItem.selectedImage = [[UIImage imageNamed:@"tab_mine_50"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    
    self.viewControllers = @[firstNav, secondNav];
}

- (void)setTabBarStyle {
    
    //去掉TabBar顶部的线
    UITabBar *tabBar = self.tabBar;
    [tabBar setShadowImage:[UIImage new]];
    [tabBar setBackgroundImage:[UIImage new]];
    tabBar.translucent = NO;

    for (UITabBarItem *item in self.tabBar.items) {
        // 设置UITabBarItem中title样式
        [item setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor lightGrayColor]} forState:UIControlStateNormal];
        [item setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor darkGrayColor]} forState:UIControlStateSelected];

        // 设置UITabBarItem中按钮大小,及img与title间距
        [item setImageInsets:UIEdgeInsetsMake(-3, 0, 3, 0)];
    }
}

- (void)initImages {
    
    _imgArr = [NSMutableArray array];
    for (int i=0; i<51; i++) {
        NSString *name = [NSString stringWithFormat:@"tab_team_%02d", i];
        UIImage *image = [UIImage imageNamed:name];
        [_imgArr addObject:image];
    }
}

- (CAAnimation *)getCustomAnimation {
    
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    //速度控制函数,控制动画运行的节奏
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    animation.duration = 0.2;
    animation.repeatCount = 1;
    animation.autoreverses = YES;
    animation.fromValue = [NSNumber numberWithFloat:0.7];
    animation.toValue = [NSNumber numberWithFloat:1.2];
    
    return animation;
}

#pragma mark - UITabBarControllerDelegate

- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController {
    
    NSInteger index = [tabBarController.childViewControllers indexOfObject:viewController];
    
    UIButton *tabBarBtn = tabBarController.tabBar.subviews[index+1];
    UIImageView *imageView = tabBarBtn.subviews.firstObject;
    
    if (index == 0) {
        [imageView stopAnimating];
        imageView.animationImages = _imgArr;
        imageView.animationRepeatCount = 1;
        imageView.animationDuration = 0.7;
        [imageView startAnimating];
    } else {
        static NSString *tabBarBtnAnimationKey = @"tabBarBtnAnimationKey";
        [imageView.layer removeAnimationForKey:tabBarBtnAnimationKey];
        [imageView.layer addAnimation:[self getCustomAnimation] forKey:tabBarBtnAnimationKey];
    }
    
    return YES;
}

@end

我们可在getCustomAnimation中定义其他类型的动画,来满足不同需求。
工程源码GitHub地址


推荐文章:
算法小专栏:谈谈大O表示法
iOS UIWebView、WKWebView注入Cookie
Cookie简介
iOS 图标&启动图生成器(一)
算法小专栏:“D&C思想”与“快速排序”
奇舞周刊