多界面开发 、 导航控制器(NavigationController)

时间:2022-04-11 17:00:40

1 VC之间的跳转和正向传值

1.1 问题

在实际的开发中更多的应用都会有多个页面组成,每个页面展示不同的信息,页面之间的跳转是由视图控制器来实现的,本案例实现两个页面的之间的跳转和页面之间的正向传值,如图-1所示:

多界面开发 、 导航控制器(NavigationController)

图-1

1.2 方案

首先使用Xcode创建一个SingleViewApplication项目,然后创建两个带有xib的视图控制器,继承至UIViewController类,分别命名为TRFirstViewController和TRSecondViewController。

在TRAppdelegate.m文件的入口方法中创建一个TRFirstViewController类对象为根视图,即当打开应用显示的第一个视图。

其次在xib中搭建好第一个视图控制器的界面,拖放一个跳转按钮和一个输入框,同样在xib中搭建好第二个视图控制器的页面,拖放一个跳转按钮和一个标签,标签是用来显示第一个页面传过来的数据。

然后在TRFirstViewController类中关联代码给第一个页面的跳转按钮添加点击事件,实现页面的跳转和传值。

最后在TRSecondViewController类中关联代码,让第二个页面的UILabel显示第一个页面传过来的数据,并给跳转按钮添加点击事件,实现跳回第一个页面的功能。

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:搭建界面

首先在已经创建好的Xcode项目中选择TRFirstViewController管理的xib文件,在xib中拖放一个按钮和一个输入框,并设置好相关的属性,为了区分页面这里给页面设置一个背景颜色,如图-2所示:

多界面开发 、 导航控制器(NavigationController)

图-2

然后选择TRSecondViewController管理的xib文件,在xib文件中拖放一个跳转按钮和一个标签,标签是用来显示第一个页面传过来的数据,设置好相关的属性,同样的给页面设置一个背景颜色,如图-3所示:

多界面开发 、 导航控制器(NavigationController)

图-3

在TRAppDelegate.m文件中,创建一个TRFirstViewController对象作为本程序的根视图,即当程序运行起来的时候内存里只有一个视图控制对象,第二个视图控制器并没有创建,代码如下所示:

 
  1. -(BOOL)application:(UIApplication*)application
  2. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  3. {
  4. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  5. self.window.backgroundColor = [UIColor whiteColor];
  6. //创建TRFirstViewController对象作为根视图
  7. self.window.rootViewController = [[TRFirstViewController alloc]initWithNibName:@"TRFirstViewController" bundle:nil];
  8. [self.window makeKeyAndVisible];
  9. return YES;
  10. }

步骤二:关联代码实现页面跳转和正向传值

将关联第一个视图的输入框关联成TRFirstViewController的私有属性inputTextField,将跳转按钮关联成方法,命名gotoSecondVC,如图-4所示:

多界面开发 、 导航控制器(NavigationController)

图-4

其次在gotoSecondVC方法里面实现页面跳转,此时需要先创建一个TRSecondViewController对象,使用系统提供的presentViewController:secondVC animated:completion:方法跳转到第二个页面,presentViewController参数就是即将跳转的目标视图控制器,这里即是刚创建的TRSecondViewController对象,此时已将第二个页面加载到内存,代码如下所示:

  1. //创建一个TRSecondViewController对象
  2. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  3. //跳转到第二个页面
  4. [self presentViewController:secondVC animated:YES completion:nil];

运行程序点击按钮就能从第一个页面跳转到第二个页面,此时内存中会存在两个视图控制器对象,第一个页面以及第一个页面管理的所有对象并不会从内存销毁。

下一步就是实现正向传值,即让第二个页面的label显示第一个页面输入框中的内容,此时第二个页面需要一个属性用来接收第一个页面传过来的数据,因此在TRSecondViewController定义一个公开的NSString类型的属性content,代码如下所示:

  1. @interface TRSecondViewController : UIViewController
  2. @property (nonatomic, strong) NSString *content;
  3. @end

然后在页面跳转之前将数据传给第二页面,注意此代码要写在跳转页面代码之前,代码如下所示:

 
  1. - (IBAction)gotoSecondVC
  2. {
  3. //创建一个TRSecondViewController对象
  4. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  5. //传参数
  6. secondVC.content = self.inputTextField.text;
  7. //跳转到第二个页面
  8. [self presentViewController:secondVC animated:YES completion:nil];
  9. }

步骤三:第二个页面显示接收的数据并实现页面跳回

在第二个页面的xib文件中,选中UILabel对象,关联成TRSecondViewController的私有属性displayLabel,然后将接收的数据在displayLabel中显示出来,注意显示的代码写在viewWillAppear:方法中,此方法每次在页面显示之前都会被调用,代码如下所示:

  1. //覆盖父类中的此方法,此方法会在每次界面显示之前调用
  2. - (void)viewWillAppear:(BOOL)animated
  3. {
  4. [super viewWillAppear:animated];
  5. //将Model中的数据显示到视图上
  6. self.displayLabel.text = self.content;
  7. }

然后关联第二页面的跳转按钮,创建方法backToFirstVC,使用系统提供的dismissViewControllerAnimated:completion:方法跳回到第一个页面,代码如下所示:

  1. - (IBAction)backToFirstVC
  2. {
  3. [self dismissViewControllerAnimated:YES completion:nil];
  4. }

此时运行程序点击跳回按钮,程序会从第二个页面跳回到第一个页面,注意此时第二个视图控制器对象会从内存中销毁,也就是第二个页面管理的所有对象此时都应该被销毁。

1.4 完整代码

本案例中,TRAppDelegate.m文件中的完整代码如下所示:

 
  1. #import "TRAppDelegate.h"
  2. #import "TRFirstViewController.h"
  3. @implementation TRAppDelegate
  4. -(BOOL)application:(UIApplication*)application
  5. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  6. {
  7. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  8. // Override point for customization after application launch.
  9. self.window.backgroundColor = [UIColor whiteColor];
  10. self.window.rootViewController = [[TRFirstViewController alloc]initWithNibName:@"TRFirstViewController" bundle:nil];
  11. [self.window makeKeyAndVisible];
  12. return YES;
  13. }
  14. @end

本案例中,TRFirstViewController.m文件中的完整代码如下所示:

 
  1. #import "TRFirstViewController.h"
  2. #import "TRSecondViewController.h"
  3. @interface TRFirstViewController ()
  4. @property (weak, nonatomic) IBOutlet UITextField *inputTextField;
  5. @end
  6. @implementation TRFirstViewController
  7. - (IBAction)gotoSecondVC
  8. {
  9. //创建一个TRSecondViewController对象
  10. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  11. //传参数
  12. secondVC.content = self.inputTextField.text;
  13. //跳转到第二个页面
  14. [self presentViewController:secondVC animated:YES completion:nil];
  15. }
  16. @end

本案例中,TRSecondViewController.h文件中的完整代码如下所示:

 
  1. #import <UIKit/UIKit.h>
  2. @interface TRSecondViewController : UIViewController
  3. @property (nonatomic, strong) NSString *content;
  4. @end

本案例中,TRSecondViewController.h文件中的完整代码如下所示:

 
  1. #import "TRSecondViewController.h"
  2. @interface TRSecondViewController ()
  3. @property (weak, nonatomic) IBOutlet UILabel *displayLabel;
  4. @end
  5. @implementation TRSecondViewController
  6. //覆盖父类中的此方法,此方法会在每次界面显示之前调用
  7. - (void)viewWillAppear:(BOOL)animated
  8. {
  9. [super viewWillAppear:animated];
  10. //将Model中的数据显示到视图上
  11. self.displayLabel.text = self.content;
  12. }
  13. - (IBAction)backToFirstVC
  14. {
  15. [self dismissViewControllerAnimated:YES completion:nil];
  16. }
  17. @end

2 从第二个VC返回到第一个VC时传值回来

2.1 问题

通过第一个案例了解了页面的之间的跳转和页面之间的正向传值,本案例将了解反向传值,即将第二页面的数据传给第一个页面,如图-5、图-6所示:

多界面开发 、 导航控制器(NavigationController)

图-5

多界面开发 、 导航控制器(NavigationController)

图-6

2.2 方案

首先使用Xcode创建一个SingleViewApplication项目,同上个案例一样创建两个带有xib的视图控制器,命名为TRFirstViewController和TRSecondViewController。在TRAppdelegate.m文件的入口方法中创建一个TRFirstViewController类对象为根视图,即当打开应用显示的第一个视图。

其次在xib中搭建好第一个视图控制器的界面,拖放一个跳转按钮和一个UILabel标签,标签是用来显示第二个页面传过来的数据,同样在xib中搭建好第二个视图控制器的页面,拖放一个跳转按钮和一个输入框,输入框用来接受用户输入的数据。

然后在TRFirstViewController类中关联代码给第一个页面的跳转按钮添加点击事件,实现页面的跳转。

最后在TRSecondViewController类中关联代码,给跳转按钮添加点击事件实现跳回第一个页面的功能,通过委托模式实现反向传值,即将第二个页面输入框中的内容传递给第一个页面,跳转完成之后第一个页面的label显示第二个页面传回的数据。

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:搭建界面

首先在已经创建好的Xcode项目中选择TRFirstViewController管理的xib文件,在xib中拖放一个按钮和一个UILabel标签,用来显示第二个页面传过来的数据,并设置好相关的属性,为了区分页面这里给页面设置一个背景颜色,如图-7所示:

多界面开发 、 导航控制器(NavigationController)

图-7

然后选择TRSecondViewController管理的xib文件,在xib文件中拖放一个跳转按钮和一个输入框,设置好相关的属性,同样的给页面设置一个背景颜色,如图-8所示:

多界面开发 、 导航控制器(NavigationController)

图-8

在TRAppDelegate.m文件中,创建一个TRFirstViewController对象作为本程序的根视图,即当程序运行起来的时候内存里只有一个视图控制对象,第二个视图控制器并没有创建,代码如下所示:

  1. -(BOOL)application:(UIApplication*)application
  2. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  3. {
  4. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  5. self.window.backgroundColor = [UIColor whiteColor];
  6. //创建TRFirstViewController对象作为根视图
  7. self.window.rootViewController = [[TRFirstViewController alloc]initWithNibName:@"TRFirstViewController" bundle:nil];
  8. [self.window makeKeyAndVisible];
  9. return YES;
  10. }

步骤二:关联代码实现页面跳转

将关联第一个视图的标签关联成TRFirstViewController的私有属性displayLabel,将跳转按钮关联成方法,命名gotoSecondVC,如图-9所示:

多界面开发 、 导航控制器(NavigationController)

图-9

其次在gotoSecondVC方法里面实现页面跳转,此时需要先创建一个TRSecondViewController对象,使用系统提供的presentViewController:secondVC animated:completion:方法跳转到第二个页面,代码如下所示:

 
  1. //创建一个TRSecondViewController对象
  2. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  3. //跳转到第二个页面
  4. [self presentViewController:secondVC animated:YES completion:nil];

步骤三:第二个页面实现页面跳回并反向传值

在第二个页面的xib文件中选中输入框,关联成TRSecondViewController的私有属性inputTextField,关联第二页面的跳转按钮,创建方法backToFirstVC,使用系统提供的dismissViewControllerAnimated:completion:方法跳回到第一个页面,代码如下所示:

  1. - (IBAction)backToFirstVC
  2. {
  3. [self dismissViewControllerAnimated:YES completion:nil];
  4. }

下一步就是实现反向传值,即让第一个页面的label显示第二个页面输入框中的内容,同上一个案例一样第一个页面需要一个属性用来接收第二个页面传过来的数据,因此在TRFirstViewController定义一个公开的NSString类型的属性content,代码如下所示:

  1. @interface TRFirstViewController : UIViewController
  2. @property (nonatomic, strong) NSString *content;
  3. @end

TRSecondViewController里面要对TRFirstViewController的属性content赋值,就需要获取到TRFirstViewController对象,所以在TRSecondViewController里面定义一个公开的TRFirstViewController类型的属性firstVC,在第一个页面的跳转方法里面对该属性进行赋值,赋值代码在跳转代码之前,代码如下所示:

  1. //TRSecondViewController.h文件里面的代码
  2. @interface TRSecondViewController : UIViewController
  3. @property (nonatomic, weak) TRFirstViewController *firstVC;
  4. @end
  5. //TRFirstViewController.m文件里面的代码
  6. - (IBAction)gotoSecondVC
  7. {
  8. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  9. //secondVC的属性firstVC进行赋值
  10. secondVC.firstVC = self;
  11. [self presentViewController:secondVC animated:YES completion:nil];
  12. }

然后TRSecondViewController.m文件中对TRFirstViewController的属性content进行赋值,注意赋值代码在跳转代码之前,代码如下所示:

 
  1. - (IBAction)backToFirstVC
  2. {
  3. self.firstVC.content = self.inputTextField.text;
  4. [self dismissViewControllerAnimated:YES completion:nil];
  5. }

最后在第一个页面将接收到的数据显示出来,显示数据的代码写在TRFirstViewController类的viewWillAppear:方法里面,每次显示页面的时候都会调用此方法,代码如下所示:

  1. - (void)viewWillAppear:(BOOL)animated
  2. {
  3. [super viewWillAppear:animated];
  4. self.displayLabel.text = self.content;
  5. }

2.4 完整代码

本案例中,TRAppDelegate.m文件中的完整代码如下所示:

 
  1. #import "TRAppDelegate.h"
  2. #import "TRFirstViewController.h"
  3. @implementation TRAppDelegate
  4. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  5. {
  6. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  7. // Override point for customization after application launch.
  8. self.window.backgroundColor = [UIColor whiteColor];
  9. self.window.rootViewController = [[TRFirstViewController alloc]initWithNibName:@"TRFirstViewController" bundle:nil];
  10. [self.window makeKeyAndVisible];
  11. return YES;
  12. }
  13. @end

本案例中,TRFirstViewController.h文件中的完整代码如下所示:

 
  1. #import <UIKit/UIKit.h>
  2. @interface TRFirstViewController : UIViewController
  3. @property (nonatomic, strong) NSString *content;
  4. @end

本案例中,TRFirstViewController.m文件中的完整代码如下所示:

 
  1. #import "TRFirstViewController.h"
  2. #import "TRSecondViewController.h"
  3. @interface TRFirstViewController ()
  4. @property (weak, nonatomic) IBOutlet UILabel *displayLabel;
  5. @end
  6. @implementation TRFirstViewController
  7. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  8. {
  9. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  10. if (self) {
  11. // Custom initialization
  12. }
  13. return self;
  14. }
  15. - (IBAction)gotoSecondVC
  16. {
  17. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  18. secondVC.firstVC = self;
  19. [self presentViewController:secondVC animated:YES completion:nil];
  20. }
  21. - (void)viewWillAppear:(BOOL)animated
  22. {
  23. [super viewWillAppear:animated];
  24. self.displayLabel.text = self.content;
  25. }
  26. @end

本案例中,TRSecondViewController.h文件中的完整代码如下所示:

 
  1. #import <UIKit/UIKit.h>
  2. #import "TRFirstViewController.h"
  3. @interface TRSecondViewController : UIViewController
  4. @property (nonatomic, weak) TRFirstViewController *firstVC;
  5. @end

本案例中,TRSecondViewController.m文件中的完整代码如下所示:

 
  1. #import "TRSecondViewController.h"
  2. @interface TRSecondViewController ()
  3. @property (weak, nonatomic) IBOutlet UITextField *inputTextField;
  4. @end
  5. @implementation TRSecondViewController
  6. - (IBAction)backToFirstVC
  7. {
  8. self.firstVC.content = self.inputTextField.text;
  9. [self dismissViewControllerAnimated:YES completion:nil];
  10. }
  11. @end

3 使用委托反向传值

3.1 问题

第二个案例采用了直接将第一个视图控制器设置为第二个控制器的属性来进行反向传值,但是这么做会使TRFirstViewController类和TRSecondViewController类的关系非常紧密,这样的程序不通用,耦合度相当高,因此更多的时候会采用第二种方法来进行页面的之间的反向传值,提高程序的通用性,降低耦合度,本案例将重点学习如何通过委托实现反向传值,界面实现效果同案例二,。

3.2 方案

本案例直接在案例二的基础上做修改,首先删除TRFirstViewController类中content属性,删除TRSecondViewController类中的firstVC属性,以及跳转方法里面的传值代码。

其次使用委托模式实现反向传值,委托模式是一种常用的设计模式,当一个对象(委托方)无法独立完成某件事(某项功能)的时候,就需要委托别的对象(被委托方)来协助共同完成,其基本步骤如下:

委托方:1. 定义委托协议;2. 定义公开的委托属性,名字叫delegate,属性的类型是id<委托协议>;3. 在适当的地方,给delegate发消息,传参数。

被委托方: 1. 遵守委托协议;2. 实现委托协议规定的某些方法;3. 将自己设置成委托方的delegate。

本案例中委托方是TRSecondViewController,被委托方是TRFirstViewController。

3.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在TRSecondViewController里面定义协议

首先删除TRFirstViewController类中content属性,删除TRSecondViewController类中的firstVC属性以及跳转方法里面的传值代码,然后在TRSecondViewController.h文件中定义一个委托协议,代码如下所示:

  1. //协议中使用了TRSecondController类,这里需要使用@class关键字提前告知有TRSecondController类存在,否则会报不存在TRSecondController类的错误
  2. @class TRSecondViewController;
  3. //1. 定义一个委托协议,命名为TRSecondViewDelegate
  4. @protocol TRSecondViewDelegate <NSObject>
  5. - (void)secondViewController:(TRSecondViewController *)secondVC message:(NSString *)message;
  6. @end

其次在TRSecondViewController类中定义一个公开的属性id<TRSecondViewDelegate>,这是一个id类型的属性,但是这个属性必须遵守TRSecondViewDelegate协议,代码如下所示:

  1. @property (nonatomic, weak)id<TRSecondViewDelegate> delegate;

步骤二:TRFirstViewController类遵守TRSecondViewDelegate

首先TRFirstViewController是被委托方,需要遵守TRSecondViewDelegate协议,代码如下所示:

  1. @interface TRFirstViewController () <TRSecondViewDelegate>

其次在TRFirstViewController.m文件中实现TRSecondViewDelegate协议中的方法,代码如下所示:

 
  1. -(void)secondViewController:(TRSecondViewController*)secondVC
  2. message:(NSString *)message
  3. {
  4. self.displayLabel.text = message;
  5. }

最后在跳转页面代码之后,给TRSecondViewController对象的delegate属性赋值,指定被委托方,将TRFirstViewControlle设置为delegate,这里不再对firstVC属性赋值了,代码如下所示:

 
  1. - (IBAction)gotoSecondVC
  2. {
  3. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  4. //将自己设置成为委托方的delegate
  5. secondVC.delegate = self;
  6. [self presentViewController:secondVC animated:YES completion:nil];
  7. }

步骤三:第二个页面实现反向传值

在TRSecondViewController类里面的跳转方法里面,通过想delegate发送消息,调用协议方法实现参数的传递,在这里就是将输入框中的内容传递过去,至于第一个页面如何使用这个参数,就由第一个页面通过实现的协议方法自行处理,代码如下所示:

 
  1. - (IBAction)backToFirstVC
  2. {
  3. //给delegate发消息,传参数
  4. [self.delegate secondViewController:self message:self.inputTextField.text];
  5. [self dismissViewControllerAnimated:YES completion:nil];
  6. }

通过以上的步骤同样实现了反向传值,这种方式的传值更常用。

3.4 完整代码

本案例中,TRAppDelegate.m文件中的完整代码如下所示:

 
  1. #import "TRAppDelegate.h"
  2. #import "TRFirstViewController.h"
  3. @implementation TRAppDelegate
  4. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  5. {
  6. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  7. // Override point for customization after application launch.
  8. self.window.backgroundColor = [UIColor whiteColor];
  9. self.window.rootViewController = [[TRFirstViewController alloc]initWithNibName:@"TRFirstViewController" bundle:nil];
  10. [self.window makeKeyAndVisible];
  11. return YES;
  12. }
  13. @end

本案例中,TRFirstViewController.m文件中的完整代码如下所示:

 
  1. #import "TRFirstViewController.h"
  2. #import "TRSecondViewController.h"
  3. //1. 遵守委托协议
  4. @interface TRFirstViewController () <TRSecondViewDelegate>
  5. @property (weak, nonatomic) IBOutlet UILabel *displayLabel;
  6. @end
  7. @implementation TRFirstViewController
  8. //2. 实现协议中的方法
  9. - (void)secondViewController:(TRSecondViewController *)secondVC message:(NSString *)message
  10. {
  11. self.displayLabel.text = message;
  12. }
  13. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  14. {
  15. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  16. if (self) {
  17. // Custom initialization
  18. }
  19. return self;
  20. }
  21. - (void)didReceiveMemoryWarning
  22. {
  23. [super didReceiveMemoryWarning];
  24. // Dispose of any resources that can be recreated.
  25. }
  26. - (IBAction)gotoSecondVC
  27. {
  28. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  29. //3. 将自己设置成为委托方的delegate
  30. secondVC.delegate = self;
  31. [self presentViewController:secondVC animated:YES completion:nil];
  32. }
  33. @end

本案例中,TRSecondViewController.h文件中的完整代码如下所示:

 
  1. #import <UIKit/UIKit.h>
  2. @class TRSecondViewController;
  3. //1. 定义委托协议
  4. @protocol TRSecondViewDelegate <NSObject>
  5. -(void)secondViewController:(TRSecondViewController*)secondVC
  6. message:(NSString *)message;
  7. @end
  8. @interface TRSecondViewController : UIViewController
  9. //2. 定义delegate属性
  10. @property (nonatomic, weak)id<TRSecondViewDelegate> delegate;
  11. @end

本案例中,TRSecondViewController.m文件中的完整代码如下所示:

 
  1. #import "TRSecondViewController.h"
  2. @interface TRSecondViewController ()
  3. @property (weak, nonatomic) IBOutlet UITextField *inputTextField;
  4. @end
  5. @implementation TRSecondViewController
  6. - (IBAction)backToFirstVC
  7. {
  8. //3. 给delegate发消息,传参数
  9. [self.delegatesecondViewController:self
  10. message:self.inputTextField.text];
  11. [self dismissViewControllerAnimated:YES completion:nil];
  12. }
  13. @end

4 导航视图控制器的基本使用

4.1 问题

通过以上三个案例学习了页面的之间的跳转和页面之间的传值,本案例将学习另一种页面的跳转方式,使用导航视图控制器(UINavigationController)实现页面的跳转,如图-10所示:

多界面开发 、 导航控制器(NavigationController)

图-10

4.2 方案

导航控制器是UIViewController的子类,是一个管理视图控制器的控制器,可以控制多个视图控制器的走向,所有被它管理的页面都会放进它的一个数组属性里面,这种方式比present方式更好更清晰。

首先使用Xcode创建一个SingleViewApplication项目,同上个案例一样创建两个带有xib的视图控制器,命名为TRFirstViewController和TRSecondViewController。在TRAppdelegate.m文件的入口方法中创建一个带有UINavigationController的TRFirstViewController类对象为根视图,即当打开应用显示的第一个视图。

其次在xib中搭建好第一个视图控制器的界面,拖放一个跳转按钮,同样在xib中搭建好第二个视图控制器的页面,并且拖放一个back按钮。

然后在TRFirstViewController类中关联代码给第一个页面的跳转按钮添加点击事件,点击按钮push出一个新的TRSecondViewController页面,并将这个页面添加到导航视图控制器管理的控制器数组(属性viewControllers)中。

在新的页面的导航栏的右侧会自动生成一个回退的按钮,点击此按钮就能实现页面的回退,当前页面则会被销毁并从导航控制器的数组(属性viewControllers)中移除。

如果希望通过自己添加的back按钮回退第一个页面,则可以给跳转按钮添加点击事件,通过pop方法回退到第一个页面,效果和自动生成的回退按钮效果一样。

4.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建一个带有导航的视图控制器作为根视图

首先TRAppDelegate.m文件中使用initWithRootViewController:初始化方法创建一个带有导航的视图控制器navi作为根视图,navi的属性viewControllers是一个数组类型,第一个元素指向firstVC,代码如下所示:

 
  1. TRFirstViewController *firstVC= [[TRFirstViewController alloc]initWithNibName:@"TRFirstViewController" bundle:nil];
  2. UINavigationController *navi= [[UINavigationController alloc]initWithRootViewController:firstVC];
  3. self.window.rootViewController = navi;

步骤二:关联代码实现推出新页面

将关联第一个视图的跳转按钮成方法命名为next,在next方法里面实现页面跳转,首先创建一个TRSecondViewController对象,使用系统提供的pushViewController:animated:方法推出一个新页面,navi会将推出的新页面添加到管理的数组中,注意推出页面的消息放送给self.navigationController对象,而不是发送给self,代码如下所示:

  1. -(IBAction)next
  2. {
  3. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  4. [self.navigationController pushViewController:secondVC animated:YES];
  5. }

然后运行程序点击按钮,即可推出新页面,新页面的左上角自动生成了一个back按钮,点击back按钮会退回第一个页面,当前页面被销毁。

如果希望通过自己添加的back按钮回退第一个页面,则可以给跳转按钮添加点击事件,通过pop方法回退到第一个页面,效果和自动生成的回退按钮效果一样,代码如下所示:

 
  1. - (IBAction)back:(id)sender
  2. {
  3. [self.navigationController popViewControllerAnimated:YES];
  4. }

4.4 完整代码

本案例中,TRAppDelegate.m文件中的完整代码如下所示:

 
  1. #import "TRAppDelegate.h"
  2. #import "TRFirstViewController.h"
  3. @implementation TRAppDelegate
  4. -(BOOL)application:(UIApplication*)application
  5. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  6. {
  7. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  8. // Override point for customization after application launch.
  9. self.window.backgroundColor = [UIColor whiteColor];
  10. TRFirstViewController *firstVC = [[TRFirstViewController alloc]initWithNibName:@"TRFirstViewController" bundle:nil];
  11. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:firstVC];
  12. self.window.rootViewController = navi;
  13. [self.window makeKeyAndVisible];
  14. return YES;
  15. }
  16. @end

本案例中,TRFirstViewController.m文件中的完整代码如下所示:

 
  1. #import "TRFirstViewController.h"
  2. #import "TRSecondViewController.h"
  3. @implementation TRFirstViewController
  4. - (IBAction)next
  5. {
  6. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  7. [self.navigationController pushViewController:secondVC animated:YES];
  8. }
  9. @end

本案例中,TRSecondViewController.m文件中的完整代码如下所示:

 
  1. #import "TRSecondViewController.h"
  2. @implementation TRSecondViewController
  3. - (IBAction)back:(id)sender
  4. {
  5. [self.navigationController popViewControllerAnimated:YES];
  6. }
  7. @end

5 为NavigationBar添加按钮

5.1 问题

通过以上案例学习了通过导航控制器实现页面的之间的跳转,本案例将学习导航控制器的导航栏(navigationBar)的设置,该案例直接在上一个案例的基础上实现,完成界面效果如图-11所示:

多界面开发 、 导航控制器(NavigationController)

图-11

5.2 方案

导航控制器有一个导航栏navigationBar位于屏幕顶部,默认情况下导航栏是现实出来的,可以在导航栏上添加按钮以实现不同的需求,按钮类型只能是UIBarButtonItem类型而不是UIButton类型。

导航控制器有一个属性navigationItem,分为左、中、右三个区域,左侧一般放置一个返回按钮或左按钮(leftBarButtonitem或者rightBarButtonItems),右侧区域一般放置一个右按钮(rightBarButtonItem或者rightBarButtonItems),中间区域是title的区域。

分别通过设置title,leftBarButtonitem或者rightBarButtonItems和rightBarButtonItem或者rightBarButtonItems等属性来实现添加标题和按钮的功能。

5.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:给导航栏添加标题

为了区分页面,给每个页面设置title,代码写在viewDidLoad方法里面,如下所示:

  1. - (void)viewDidLoad
  2. {
  3. [super viewDidLoad];
  4. self.title = @"FirstVC";
  5. }

同样的方式给第二个页面设置标题为SecondVC,界面效果如图-12所示:

多界面开发 、 导航控制器(NavigationController)

图-12

步骤二:给导航栏添加按钮

首先给第一个视图的导航栏设置一个右按钮,使用initWithTitle: style: target: action:初始化方法创建一个UIBarButtonItem类型的对象,设置为导航栏的右按钮,点击实现推出新页面功能,style参数是一个枚举类型UIBarButtonSystemItem,系统已经提供了几种按钮类型以供使用,代码如下所示:

 
  1. self.navigationItem.rightBarButtonItem= [[UIBarButtonItemalloc]initWithTitle:@"Right"style:UIBarButtonItemStyleDone target:self action:@selector(next)];

然后添加两个左按钮,使用initWithTitle: style: target: action:初始化方法创建两个UIBarButtonItem类型的对象,封装成数组赋值给leftBarButtonItems,代码如下所示:

 
  1. UIBarButtonItem *item1 = [[UIBarButtonItem alloc]initWithTitle:@"B1" style:UIBarButtonItemStyleBordered target:nil action:nil];
  2. UIBarButtonItem *item2 = [[UIBarButtonItem alloc]initWithTitle:@"B2" style:UIBarButtonItemStyleBordered target:nil action:nil];
  3. self.navigationItem.leftBarButtonItems = @[item1, item2];

运行程序,完成效果如图-13所示:

多界面开发 、 导航控制器(NavigationController)

图-13

5.4 完整代码

本案例中,TRFirstViewController.m文件中viewDidLoad方法中的完整代码如下所示:

 
  1. - (void)viewDidLoad
  2. {
  3. [super viewDidLoad];
  4. //设置标题
  5. self.title = @"FirstVC";
  6. //设置左按钮
  7. UIBarButtonItem *item1 = [[UIBarButtonItem alloc]initWithTitle:@"B1" style:UIBarButtonItemStyleBordered target:nil action:nil];
  8. UIBarButtonItem *item2 = [[UIBarButtonItem alloc]initWithTitle:@"B2" style:UIBarButtonItemStyleBordered target:nil action:nil];
  9. self.navigationItem.leftBarButtonItems = @[item1, item2];
  10. //设置右按钮,点击推出新页面
  11. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"Right" style:UIBarButtonItemStyleDone target:self action:@selector(next)];
  12. }

6 界面中显示ToolBar,显示一个简易的播放工具条

6.1 问题

导航控制器除了有一个导航栏还有一个工具栏(toolbar),本案例将学习如何设置工具栏,制作一个简易的播放工具条,该案例直接在上一个案例的基础上实现,完成界面效果如图-14所示:

多界面开发 、 导航控制器(NavigationController)

图-14

6.2 方案

导航控制器除了有一个导航栏navigationBar工具栏还有一个工具栏toolbar位于屏幕底部,在默认情况下是隐藏的,所以首先让工具栏显示出来。

然后在工具栏上添加按钮以实现不同的需求,按钮类型同导航栏一样只能是UIBarButtonItem类型而不是UIButton类型。

6.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:让工具栏显示出来

导航控制器有一个属性toolbarHidden,用来控制工具栏的显示,该属性的设置需要写在viewWillAppear:方法中,作用是每次显示该页面的时候都显示工具栏,代码如下所示:

 
  1. - (void)viewWillAppear:(BOOL)animated
  2. {
  3. [super viewWillAppear:animated];
  4. //设置ToolBar的显示
  5. self.navigationController.toolbarHidden = NO;
  6. }

步骤二:分别创建后退、暂停、播放和快进按钮

使用initWithBarButtonSystemItem:target:action:初始化方法创建按钮,BarButtonSystemItem参数是一个枚举类型UIBarButtonSystemItem,系统已经提供了几种按钮类型以供使用,后退是UIBarButtonSystemItemRewind,暂停是UIBarButtonSystemItemPause,播放是UIBarButtonSystemItemPlay,快进是UIBarButtonSystemItemFastForward,代码如下所示:

 
  1. UIBarButtonItem *b1= [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemRewind target:nil action:nil];
  2. UIBarButtonItem *b2 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemPause target:nil action:nil];
  3. UIBarButtonItem *b3 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:nil action:nil];
  4. UIBarButtonItem *b4 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFastForwardtarget:nil action:nil];

每个按钮之间需要使用固定空白FixedSpace和可变空白FlexibleSpace两种控件隔开,FixedSpace填补固定空白,FlexibleSpace尽可能的填补空白,代码如下所示:

 
  1. //固定空白按钮
  2. UIBarButtonItem *fixed = [[ UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
  3. fixed.width = 15;
  4. //可变空白按钮
  5. UIBarButtonItem *flexible = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];

步骤三:将按钮添加到工具栏上

将创建好的按钮和空白控件按先后顺序封装成一个数组,给属性toolbarItems赋值,代码如下所示:

 
  1. self.toolbarItems = @[fixed, b1, flexible, b2, flexible, b3, flexible,b4,fixed];

运行程序,可见界面上成功添加播放条工具栏。

6.4 完整代码

本案例中,TRFirstViewController.m文件中viewDidLoad方法中的完整代码如下所示:

 
  1. - (void)viewDidLoad
  2. {
  3. [super viewDidLoad];
  4. self.title = @"FirstVC";
  5. UIBarButtonItem *item1 = [[UIBarButtonItem alloc]initWithTitle:@"B1" style:UIBarButtonItemStyleBordered target:nil action:nil];
  6. UIBarButtonItem *item2 = [[UIBarButtonItem alloc]initWithTitle:@"B2" style:UIBarButtonItemStyleBordered target:nil action:nil];
  7. self.navigationItem.leftBarButtonItems = @[item1, item2];
  8. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"Right" style:UIBarButtonItemStyleDone target:self action:@selector(next)];
  9. UIBarButtonItem *b1 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemRewind target:nil action:nil];
  10. UIBarButtonItem *b2 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemPause target:nil action:nil];
  11. UIBarButtonItem *b3 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:nil action:nil];
  12. UIBarButtonItem *b4 = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFastForward target:nil action:nil];
  13. //固定空白按钮
  14. UIBarButtonItem *fixed = [[ UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
  15. fixed.width = 15;
  16. //可变空白按钮
  17. UIBarButtonItem *flexible = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
  18. self.toolbarItems = @[fixed, b1, flexible, b2, flexible, b3, flexible,b4,fixed];
  19. }

7 用Modal方式展示另一个NavigationController

7.1 问题

在实际应用中很多时候会用到以Modal的形式跳转到一个导航控制器,本案例将模拟一个通讯录添加新联系人的界面,完成界面效果如图-15所示:

多界面开发 、 导航控制器(NavigationController)

图-15

7.2 方案

本案例的根视图控制器是一个导航控制器,管理着TRFirstViewController,第一个界面的导航栏上面左按钮是群组按钮,右按钮是添加联系人的按钮,可以使直接使用系统提供的按钮样式UIBarButtonSystemItemAdd,标题是“所有联系人”。

当点击添加联系人按钮时会以modal的形式跳转到第二个界面,但是直接跳转过去,第二个界面是不带导航栏的,因为第二个视图控制器并不是根视图控制器push出来的,并不在根视图控制器管理的控制器数组中,因此需要在跳转的时候新创建一个导航控制器来管理第二个视图控制器。

最后在第二个界面的导航栏上添加左按钮取消、右按钮完成和标题“新联系人”,点击取消按钮回退到第一个视图控制器。

7.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:搭建第一个视图界面,给导航栏添加按钮

同案例4一样,在TRAppDelegate.m文件中使用initWithRootViewController:初始化方法创建一个带有导航的视图控制器navi作为根视图,在TRFirstViewController的viewDidLoad方法里面给导航栏添加按钮和标题,点击右按钮调用方法add:实现页面跳转,代码如下所示:

 
  1. - (void)viewDidLoad
  2. {
  3. [super viewDidLoad];
  4. //设置导航栏标题
  5. self.title = @"所有联系人";
  6. //设置导航栏的左按钮
  7. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"群组" style:UIBarButtonItemStylePlain target:nil action:nil];
  8. //设置导航栏的右按钮,点击调用add:方法实现页面跳转
  9. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add:)];
  10. }

完成效果如图-16所示:

多界面开发 、 导航控制器(NavigationController)

图-16

步骤二:实现add:方法跳转页面

首先在add:方法里面需要创建一个TRSecondViewController对象secondVC,然后新创建一个导航控制器navi来管理secondVC,此时的navi和根视图的导航控制器是两个不同的对象,最后使用present方法展现新的页面,代码如下所示:

 
  1. -(void)add:(UIBarButtonItem *)buttonItem
  2. {
  3. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  4. //创建新的导航控制器,管理secondVC
  5. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:secondVC];
  6. //使用present方式展示新的页面
  7. [self.navigationController presentViewController:navi animated:YES completion:nil];
  8. }

步骤三:给第二个页面的导航栏添加按钮和标题

在TRSecondViewController的viewDidLoad方法里面给导航栏添加按钮和标题,点击取消按钮调用方法cancel:方法,回退到第一个页面,代码如下所示:

 
  1. - (void)viewDidLoad
  2. {
  3. [super viewDidLoad];
  4. self.title = @"新联系人";
  5. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"完成" style:UIBarButtonItemStyleDone target:nil action:nil];
  6. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"取消" style:UIBarButtonItemStylePlain target:self action:@selector(cancel)];
  7. }

完成效果如图-17所示:

多界面开发 、 导航控制器(NavigationController)

图-17

然后实现cancel方法,使用dismissViewControllerAnimated:YES completion:方法回退到第一个界面,代码如下所示:

  1. - (void)cancel
  2. {
  3. [self.navigationController dismissViewControllerAnimated:YES completion:nil];
  4. }

7.4 完整代码

本案例中,TRAppdelegate.m文件中完整代码如下所示:

 
  1. #import "TRAppDelegate.h"
  2. #import "TRFirstViewController.h"
  3. @implementation TRAppDelegate
  4. -(BOOL)application:(UIApplication*)application
  5. didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  6. {
  7. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  8. self.window.backgroundColor = [UIColor whiteColor];
  9. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:[[TRFirstViewController alloc]initWithNibName:@"TRFirstViewController" bundle:nil]];
  10. self.window.rootViewController = navi;
  11. [self.window makeKeyAndVisible];
  12. return YES;
  13. }
  14. @end

本案例中,TRFirstViewController.m文件中的完整代码如下所示:

 
  1. #import "TRFirstViewController.h"
  2. #import "TRSecondViewController.h"
  3. @implementation TRFirstViewController
  4. - (void)viewDidLoad
  5. {
  6. [super viewDidLoad];
  7. self.title = @"所有联系人";
  8. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"群组" style:UIBarButtonItemStylePlain target:nil action:nil];
  9. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(add:)];
  10. }
  11. - (void)add:(UIBarButtonItem *)buttonItem
  12. {
  13. TRSecondViewController *secondVC = [[TRSecondViewController alloc]initWithNibName:@"TRSecondViewController" bundle:nil];
  14. //[self.navigationController pushViewController:secondVC animated:YES];
  15. UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:secondVC];
  16. [self.navigationController presentViewController:navi animated:YES completion:nil];
  17. }
  18. @end

本案例中,TRSecondViewController.m文件中的完整代码如下所示:

 
  1. #import "TRSecondViewController.h"
  2. @implementation TRSecondViewController
  3. - (void)viewDidLoad
  4. {
  5. [super viewDidLoad];
  6. self.title = @"新联系人";
  7. self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"完成" style:UIBarButtonItemStyleDone target:nil action:nil];
  8. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"取消" style:UIBarButtonItemStylePlain target:self action:@selector(cancel)];
  9. }
  10. - (void)cancel
  11. {
  12. [self.navigationController dismissViewControllerAnimated:YES completion:nil];
  13. }
  14. @end

相关文章