IOS 开发(二) —— 实现 TabBar与页面跳转

2,038 阅读6分钟

一、 前言

上节我们学到了 UIView 和 UIViewController,UIView管理视图的单元, UIViewController 可以包含多个 UIView, 但是如果想实现管理多个页面, 那么我们就需要更高级的 UIViewController 来管理多个 UIViewController

常用的APP页面结构如下

image.png

二、UITabBarController

常用的APP底部通常会有四五个按钮,可以使用 UITabBarController ,他包含了 TabBar 和 ViewControllers,UITabBarController 功能就是管理多个 UIViewController 切换,点击底部对应按钮, 选中对应按钮,切换内部的 ViewController

2-1、TabBar的实现

首先先看一下内置的 TabBar 文件结构, 在xcode中按下Command + Shift + O快捷键来唤起“Open Quickly”搜索框,搜索 UITabBarController

image.png

可以看到 UITabBarController 继承于UIViewController, 内置了 UITabBar,提供了 setViewControllers 方法

2-2、单一视图切换为多视图管理

首先通过设置去掉项目自身的设置。由默认的 Main 来决定视图,改为手动接管 image.png

去掉设置后,运行会发现界面黑屏, 我们需要代码指定视图,新版的视图修改在 SceneDelegate 中, 我们先实现单一页面,先去展示 ViewController 代码如下:

#import "SceneDelegate.h"
#import "ViewController.h"
@interface SceneDelegate ()
@end
@implementation SceneDelegate
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
   UIWindowScene *windowScene = (UIWindowScene *)scene;
   self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
   self.window.rootViewController    = [[ViewController alloc] init];
   self.window.backgroundColor       = [UIColor whiteColor];
   [self.window makeKeyAndVisible];
}
@end

写完这个运行,会有如下报错, 我们还需要在 SceneDelegate.m 中加入

image.png

**@property** (**strong**, **nonatomic**) UIWindow * window;

界面现在可以正常展示了,然后可以改为用 TabBar 的多页面视图了, 修改 SceneDelegate.h 文件的

- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
    UIWindowScene *windowScene = (UIWindowScene *)scene;
    self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
    
    UITabBarController *tabbarController = [[UITabBarController alloc] init];
    UIViewController *controller1 = [[UIViewController alloc] init];
    controller1.tabBarItem.title = @"新闻";
    controller1.tabBarItem.image = [UIImage imageNamed:@"home"];
    controller1.tabBarItem.selectedImage = [UIImage imageNamed:@"home-active"];
    controller1.view.backgroundColor = [UIColor redColor];
    UIViewController *controller2 = [[UIViewController alloc] init];
    controller2.tabBarItem.title = @"视频";  
    controller2.tabBarItem.image = [UIImage imageNamed:@"expore"];
    controller2.tabBarItem.selectedImage = [UIImage imageNamed:@"expore-active"];
    controller2.view.backgroundColor = [UIColor yellowColor];
    UIViewController *controller3 = [[UIViewController alloc] init];
    controller3.view.backgroundColor = [UIColor greenColor];
    controller3.tabBarItem.title = @"推荐";
    controller3.tabBarItem.image = [UIImage imageNamed:@"setting"];
    controller3.tabBarItem.selectedImage = [UIImage imageNamed:@"setting-active"];
    UIViewController *controller4 = [[UIViewController alloc] init];
    controller4.view.backgroundColor = [UIColor lightGrayColor];
    controller4.tabBarItem.title = @"我的";
    controller4.tabBarItem.image = [UIImage imageNamed:@"setting"];
    controller4.tabBarItem.selectedImage = [UIImage imageNamed:@"setting-active"];
    [tabbarController setViewControllers:@[controller1,controller2,controller3,controller4]];
    
    //  单页面应用
    // self.window.rootViewController    = [[ViewController alloc] init];
    // TabBar 多页面应用
    self.window.rootViewController = tabbarController;
    self.window.backgroundColor       = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
}

图片放在Assets中, icon 展示的顺序 由setViewControllers里面控制。

效果如下:

image.png

自定义的视图切换,可以使用开源,也可以自己实现, 主要还是利用系统的自己再此基础上贴一个自己的tabbar逻辑

三、UINavigationController 管理页面

UINavigationController 内部存在一个页面栈结构,通常只展示栈顶页面,Push/Pop操作,进行页面的切换操作

UINavigationController 与 TabBarController 一样 也是内部维护了一个 UIViewController, 处理 UIViewController的切换

image.png

  • 使用系统函数实现
  • 相关开源框架和项目
    • WRNavigationBar
    • KMNavigationBarTransition
    • RTRootNavigationController

3-1、 改造 controller1

引入 #import "ViewController.h"

// 创建 UINavigationController 跟视图设置为 ViewController
ViewController *viewController = [[ViewController alloc] init];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController: viewController];
navigationController.view.backgroundColor = [UIColor redColor];
navigationController.tabBarItem.title = @"新闻";
navigationController.tabBarItem.image = [UIImage imageNamed:@"home"];
navigationController.tabBarItem.selectedImage = [UIImage imageNamed:@"home-active"];

//...

// 给TabBar设置一个背景色
tabbarController.tabBar.backgroundColor = [UIColor orangeColor];
// 用 navigationController 替换 controller1
[tabbarController setViewControllers:@[navigationController,controller2,controller3,controller4]];

设置 navigationController 的根view为,ViewController 的内容,然后设置到 tabBarController 上面

image.png

3-2、设置跳转页面逻辑

在 ViewController 视图中增加如下代码

- (void)viewDidLoad {
      [super viewDidLoad];
      self.view.backgroundColor = [UIColor whiteColor];
      TestView *view  = [[TestView alloc] init];
      view.backgroundColor = [UIColor greenColor];
      view.frame = CGRectMake(110, 150, 100, 100);
      [self.view addSubview:view];
      // 给view 添加手势动作,触发执行navigation的push view操作
      UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(pushController)];
      [view addGestureRecognizer:tapGesture];
}

// 执行的方法
-(void)pushController{
  [self.navigationController pushViewController:[[UIViewController alloc] init] animated:YES];
}

在view上添加手势,触发 navagationController 的push 跳转操作,跳转到新创建的 UIViewController 上

output1.gif

系统会给自动给下一层增加一个返回按钮

设置更多跳转后页面内容

// 执行的方法
-(void)pushController{
  // 新建一个viewControll 视图
  UIViewController *viewController =  [[UIViewController alloc] init];
  // 添加背景色
  viewController.view.backgroundColor = [UIColor whiteColor];
  // 设置当前view的title文字
  viewController.navigationItem.title = @"内容";
  // 设置右侧文字内容
  viewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"右侧标题" style:UIBarButtonItemStylePlain target:self action:nil];
  // 添加到navigation跳转view逻辑上
  [self.navigationController pushViewController:viewController animated:YES];
}

output1.gif

通过cmd+shift+o 查看navigationItem 可以查看更多属性

四、UIWindow 窗口

  • 特殊形式的UIView,提供App中展示内容的基础窗口
  • 只作为一个容器,和 ViewController 一起协同工作
  • 通常屏幕上只存在、展示一个 UIWindow
  • 使用 storyborad 会帮我们自动创建
  • 手动创建
    • 创建 UIWindow
    • 设置 rootViewController
    • makeKeyAndVisible

image.png

目前,新创建的项目,会自动给创建一个 storyborad 帮我们创建一个视图。也就是项目目录中的 Main.storyborad 为了更好的理解,我们需要设置去掉 storyborad 帮我们自动创建window 的逻辑,手动进行创建,这点在上面已经演示了如何删除

在AppAppDelegate.m 中创建一个 window,只需要三步, 创建、设置到rootview上,展示, 在SceneDelegate.m 上创建也是一样的道理, 只是后面为了更好的分离逻辑,进行了分离,写在哪里都是可以的。我们上文的 tabbar 的视图就是写在了 在SceneDelegate.m 中

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // Override point for customization after application launch.
  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  ViewController *viewController = [[ViewController alloc] init];
  self.window.rootViewController = viewController;
  [self.window makeKeyAndVisible];
  return YES;
}

image.png

五、改造 Tabbar

上面实现的 tabbar 有一个问题, 就是页面切换的时候, 页面的跳转是收敛在 第一个 tab 里的, 这是不符合体验的

改造过程也很简单, 让 rootViewController 指向 navigationController, 然后修改navigationController的根元素为 tabbarController, 在设置 tabbarController 的第一个 item 为ViewController 即可, 代码如下

  • SceneDelegate.m 文件
#import "SceneDelegate.h"
#import "ViewController.h"
@interface SceneDelegate ()
@end
@implementation SceneDelegate

- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
    UIWindowScene *windowScene = (UIWindowScene *)scene;
    self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
    UITabBarController *tabbarController = [[UITabBarController alloc] init];
    // 创建 UINavigationController 跟视图设置为 ViewController
    ViewController *viewController = [[ViewController alloc] init];
//    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController: viewController];
//    navigationController.view.backgroundColor = [UIColor redColor];
//    navigationController.tabBarItem.title = @"新闻";
//    navigationController.tabBarItem.image = [UIImage imageNamed:@"home"];
//    navigationController.tabBarItem.selectedImage = [UIImage imageNamed:@"home-active"];
    UIViewController *controller1 = viewController; // [[UIViewController alloc] init];
    controller1.view.backgroundColor = [UIColor redColor];
    controller1.tabBarItem.title = @"新闻";
    controller1.tabBarItem.image = [UIImage imageNamed:@"home"];
    controller1.tabBarItem.selectedImage = [UIImage imageNamed:@"home-active"];
    UIViewController *controller2 = [[UIViewController alloc] init];
    controller2.view.backgroundColor = [UIColor yellowColor];
    controller2.tabBarItem.title = @"视频";
    controller2.tabBarItem.image = [UIImage imageNamed:@"expore"];
    controller2.tabBarItem.selectedImage = [UIImage imageNamed:@"expore-active"];
    UIViewController *controller3 = [[UIViewController alloc] init];
    controller3.view.backgroundColor = [UIColor greenColor];
    controller3.tabBarItem.title = @"推荐";
    controller3.tabBarItem.image = [UIImage imageNamed:@"setting"];
    controller3.tabBarItem.selectedImage = [UIImage imageNamed:@"setting-active"];
    UIViewController *controller4 = [[UIViewController alloc] init];
    controller4.view.backgroundColor = [UIColor lightGrayColor];
    controller4.tabBarItem.title = @"我的";
    controller4.tabBarItem.image = [UIImage imageNamed:@"setting"];
    controller4.tabBarItem.selectedImage = [UIImage imageNamed:@"setting-active"];
    // 给TabBar设置一个背景色
    tabbarController.tabBar.backgroundColor = [UIColor orangeColor];
    // 用 navigationController 替换 controller1
    [tabbarController setViewControllers:@[controller1,controller2,controller3,controller4]];
    // 生成navgationcontroller
    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController: tabbarController];
    //  单页面应用
    // self.window.rootViewController    = [[ViewController alloc] init];
    // TabBar 多页面应用
    // self.window.rootViewController = tabbarController;
    // 改造后的tabbar 跳转到新的页面
    self.window.rootViewController = navigationController;
    
    [self.window makeKeyAndVisible];
}
@end

output1.gif

六、delegate 设计模式

UITabBarController 自动处理点击页面切换,就是内部进行了处理,系统的组件也会通过delegate 暴露一些事件方法,提供给开发者来利用做一些特殊逻辑处理。

对于使用者来说, 可以设置 delegate = self 按需实现方法

对于设计者来说,可以自定义操作,如 @optional/@required 注解,提供 @property - delegate 在对应时机,让delegate 执行对应方法

以 TabBarController 为例

设置当前组件进行接收 delegate 的方法

// 调用delegate 的方法
tabbarController.delegate = self;

设置后,会有一个提示,需要将 UITabBarControllerDelegate 在当前组件进行声明 image.png

@interface SceneDelegate ()<UITabBarControllerDelegate>

声明之后,可以右键 UITabBarControllerDelegate 进入它的内部, 查看有哪些方法

image.png

简单使用一下

- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
    NSLog(@"did select");
}

当执行切换tab的时候会触发

image.png

七、总结

本文通过实现 TabBar 需求学习了视图 UIView、 UIViewController、Window 的关系, 学习了navagationController 与 tabbarController,学习了如何使用 delegate 调用系统内部方法