IOS 学习笔记之基于 UITabBarController 的主流 APP 底部导航栏实现

前言

当app中有多个控制器的时候,就需要对这些控制器进行管理,用一个控制器去管理其他多个控制器;如图所示:

IOS UIView 提供了两个特殊的控制器,UINavigationController和UITabBarController去管理其它控制器。

本文以下面的一个简单例子加以说明基本用法:


UIWindow 和 UIViewController 的基本内容

新建一个 Single View Application 工程,在iOS应用中,每个程序得main函数中都调用了UIApplicationMain函数。

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

先来看看UIApplicationMain函数的原型:

UIKIT_EXTERN int UIApplicationMain(
  int argc, 
  char *argv[], 
  NSString * __nullable principalClassName, 
  NSString * __nullable delegateClassName
);

前面的argc和argv是ISO C标准的main函数的参数,直接传递给UIApplicationMain进行相关处理,principalClassName是应用程序类的名字,该类必须继承自UIApplication类,而delegateClassName是应用程序类的代理类。如果主要nib文件(在info.plist文件中指定,key是NSMainNibFile)存在,就会在nib文件对象里寻找Application对象和连接它的delegate。此函数会根据principalClassName创建UIApplication对象,然后根据delegateClassName创建一个delegate对象,并将UIApplication对象中的delegate属性设置为delegate对象,接着会建立应用的main runloop,进行事件的处理,首先调用application:didFinishLaunchingWithOptions。程序正常退出时才返回(如今iOS支持后台运行,系统在必要时会强行杀死不用的进程,一般这个函数不等返回进程就终止了)。

// 程序启动完成调用
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 创建Window
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // 设置Window的背景颜色
    self.window.backgroundColor = [UIColor whiteColor];
    
    // 设置根控制器
    ViewController *vc = [[ViewController alloc] init];
    self.window.rootViewController = vc;

    // 设置并显示主窗口
    [self.window makeKeyAndVisible];
    
    return YES;
}

UIWindow是一种特殊的UIView,通常在一个应用中只会有一个UIWindow。在ios程序启动完成后,建立的第一个视图控件就是UIWindow,接着创建一个控制器的View,最后将控制器的View添加到UIWindow上,于是控制器的View就是显示到屏幕上了。一个ios程序之所以能显示在屏幕上,完全是因为它有UIWindow,也就是说没有UIWindow就看不到任何UI界面。

控制器的创建:

UIViewController *vc = [UIViewController alloc] init];

可以用isViewLoaded方法判断一个UIViewController的view是否已经被加载;控制器的view加载完毕就会调用viewDidLoad方法。

将view添加到UIWindow:

  • 直接将view添加到UIWindow中,并不理会view对应的控制器。
- (void)addSubView:(UIView *)view;
  • 通过设置根控制器,自动将rootViewController的view添加到UIWindow中,负责管理rootViewController的生命周期。
@property(nonatomic,retain) UIViewController *rootViewController;

获取 RootViewController:

第一种方法:

UIWindow *window = [UIApplication sharedApplication].keyWindow;
UIViewController *rootViewController = window.rootViewController;

第二种方法:

AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
UIViewController *rootViewController = appdelegate.window.rootViewController;

UINavigationController 和 UITabBarController 的基本内容

UINavigationController以栈的形式保存子控制器,使用push方法能将某个控制器压入栈,使用pop方法可以移除栈顶控制器。

打开 ViewController 的方式:

 - (void)pushViewController:(UIViewController*)viewController animated:(BOOL)animated;

三种移除 ViewController 的方式:

将栈顶的控制器移除
- (UIViewController*)popViewControllerAnimated:(BOOL)animated;
回到指定的子控制器
- (NSArray*)popToViewController:(UIViewController*)viewController animated:(BOOL)animated;
回到根控制器(栈底控制器)
- (NSArray*)popToRootViewControllerAnimated:(BOOL)animated;

初始化UINavigationController:

方法一:

UINavigationController *nv = [[UINavigationController alloc] init];  

方法二:

UINavigationController *nv = [[UINavigationController alloc] initWithRootViewController:rootViewController];

UITabBarController和UINavigationController类似,UITabBarController也可以轻松地管理多个控制器,轻松完成控制器之间的切换,典型的例子就是QQ、微信等应⽤。但是UITabBarController其管理的视图一直存在,而UINavigationController在pop后会销毁掉,释放内存。UITabBarController通常作为整个程序的rootViewController,而且不能添加到别的container viewController中。

UITabBarController的使用步骤:

  1. 初始化UITabBarController
  2. 创建子控制器(viewcontroller)
  3. 把子控制器添加到UITabBarController
  4. 设置UIWindow的rootViewController为UITabBarController

将子控制器添加到UITabBarController:

方法一:添加单个子控制器

- (void)addChildViewController:(UIViewController*)childController;

方法二:添加多个子控制器

view.viewControllers = NSArray *childController;

UITabBarController中嵌套UINavigationController:

UINavigationController中嵌套UITabBarController:

项目实战

将UITabBarController的逻辑提取到一个独立的ViewController中。可以新建一个继承于UITabBarController的ViewController。

AppDelegate.m:

// 程序启动完成调用
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 创建Window
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // 设置Window的背景颜色
    self.window.backgroundColor = [UIColor whiteColor];
    
    // 设置根控制器
    ViewController *vc = [[ViewController alloc] init];
    self.window.rootViewController = vc;

    // 设置并显示主窗口
    [self.window makeKeyAndVisible];
    
    return YES;
}

ViewController.h:

@interface ViewController : UITabBarController

@end

ViewController.m:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 创建子控制器
    HomeViewController *homeVC=[[HomeViewController alloc] init];
    [self setTabBarItem:homeVC.tabBarItem
                  title:@"首页"
              titleSize:13.0
          titleFontName:@"HeiTi SC"
          selectedImage:@"i_tab_home_selected"
     selectedTitleColor:[UIColor redColor]
            normalImage:@"i_tab_home_normal"
       normalTitleColor:[UIColor grayColor]];
    
    BlogViewController *blogVC=[[BlogViewController alloc] init];
    [self setTabBarItem:blogVC.tabBarItem
                  title:@"博文"
              titleSize:13.0
          titleFontName:@"HeiTi SC"
          selectedImage:@"i_tab_blog_selected"
     selectedTitleColor:[UIColor redColor]
            normalImage:@"i_tab_blog_normal"
       normalTitleColor:[UIColor grayColor]];
    
    UINavigationController *homeNV = [[UINavigationController alloc] initWithRootViewController:homeVC];
    UINavigationController *blogNV = [[UINavigationController alloc] initWithRootViewController:blogVC];
    // 把子控制器添加到UITabBarController
    self.viewControllers = @[homeNV, blogNV];
}

我们通常需要对tabBarItem进行设置,可以封装一个setTabBarItem方法,只需要进行简单的配置即可。

- (void)setTabBarItem:(UITabBarItem *)tabbarItem
                title:(NSString *)title
            titleSize:(CGFloat)size
        titleFontName:(NSString *)fontName
        selectedImage:(NSString *)selectedImage
   selectedTitleColor:(UIColor *)selectColor
          normalImage:(NSString *)unselectedImage
     normalTitleColor:(UIColor *)unselectColor
{
    
    //设置图片
    tabbarItem = [tabbarItem initWithTitle:title image:[[UIImage imageNamed:unselectedImage]imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] selectedImage:[[UIImage imageNamed:selectedImage]imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
    
    // S未选中字体颜色
    [[UITabBarItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName:unselectColor,NSFontAttributeName:[UIFont fontWithName:fontName size:size]} forState:UIControlStateNormal];
    
    // 选中字体颜色
    [[UITabBarItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName:selectColor,NSFontAttributeName:[UIFont fontWithName:fontName size:size]} forState:UIControlStateSelected];
}

然后对每个子ViewController进行自定义即可,如BlogViewController.h可以这样写:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIButton *helloBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, SCREEN_WIDTH - 200, 50)];
    helloBtn.backgroundColor = [UIColor redColor];
    [helloBtn setTitle:@"hello world" forState:UIControlStateNormal];
    [helloBtn addTarget:self action:@selector(showToast) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:helloBtn];
}

- (void)showToast{
    // 打开新ViewController
    BlogDetailViewController *blogDetail = [[BlogDetailViewController alloc] init];
    blogDetail.hidesBottomBarWhenPushed=YES;
    [self.navigationController pushViewController:blogDetail animated:YES];
}

源代码:https://github.com/zhaomenghuan/learn-ios/tree/master/example/UITabBarController

参考

iOS -App主流框架UINavigationController && UITabBarController的简单使用


我最近在 segmentfault 开了5+ App开的系列讲座,欢迎前来围观:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容