iOS开发中界面跳转是必不可少的一个功能,一个完整的App必然是由多个界面组成的。我在这篇博客中将会较为全面的讲解在是否有导航的情况下进行跳转以及返回,并且对modal、push、custom等视图做一个介绍。代码我已经上传至:https://github.com/chenyufeng1991/NavigationTest 。 之前我也写过一篇关于导航栏的博客《iOS纯代码实现界面建立、跳转、导航栏、(无storyboard,无nib)》。大家也可以进行参考。
个人建议,如果你的项目已经确定要使用storyboard的情况下,可以自动在storyboard中生成导航栏,并且界面的跳转都可以通过pushViewController这种方式来实现。这样可以大大提高效率和减少代码量,也是目前苹果提倡的方式。
但是如果是在团队项目开发中,可能有人会有顾忌,就是怕大家一起操作storyboard,会导致各种冲突,所以退而求其次,选用了xib来构建界面。这也是没有问题的,我今天将会来实现使用代码导航栏和不同的视图的跳转。
下面我列出界面跳转中最常用14个情况,并给出了该方法是否可行。
(1)storyboard生成ViewController并添加按钮+代码生成navigationController+使用pushpushViewController跳转到另一个storyboard生成的ViewController;-->该按钮无法点击,无法进行跳转;
(2)storyboard生成ViewController并添加按钮+代码生成navigationController+使用presentViewController跳转到另一个storyboard生成的ViewController;-->该按钮无法点击,无法进行跳转;
(3)storyboard生成ViewController并添加按钮+storyboard中Action Segue使用push跳转到另一个storyboard生成的ViewController;-->报错,无法跳转,提示:“Push segues can only be used when the source controller is managed by an instance of UINavigationController.”;
(4)storyboard生成ViewController并添加按钮+storyboard中Action Segue使用modal跳转到另一个storyboard生成的ViewController;-->正常,可以进行跳转;
(5)storyboard生成ViewController并添加按钮+storyboard中在第一个ViewController中生成导航栏+storyboard中Action Segue使用push跳转到另一个storyboard生成的ViewController;-->正常,可以进行跳转,并可以使用导航栏自带的返回按钮返回上一个页面;
(6)storyboard生成ViewController并添加按钮+storyboard中在第一个ViewController中生成导航栏+storyboard中Action Segue使用modal跳转到另一个storyboard生成的ViewController;-->跳转正常,但是第二个界面不会出现导航栏,该方法不可行;
(7)storyboard生成ViewController并添加按钮+storyboard在第一个ViewController中生成导航栏+使用pushViewController跳转到另一个storyboard生成的ViewController;-->跳转正常,但中间会出现卡顿,第二个界面会出现黑屏,该方法不可行;
(8)storyboard生成ViewController并添加按钮+storyboard在第一个ViewController中生成导航栏+使用presentViewController跳转到另一个storyboard生成的ViewController;-->跳转正常,第二个界面是黑屏,该方法不可行;
(9)storyboard生成ViewController并添加按钮+storyboard中不添加任何导航栏+使用presentViewController跳转到另一个xib生成的ViewController;-->正常,可以进行跳转;
(10)storyboard生成ViewController并添加按钮+storyboard中不添加任何导航栏+使用pushViewController跳转到另一个xib生成的ViewController;-->按钮可以进行点击,但没有任何响应,不能进行跳转;
(11)storyboard生成ViewController并添加按钮+storyboard中添加导航栏+使用presentViewController跳转到另一个xib生成的ViewController;-->跳转正常,但是第二个界面不会出现导航栏,该方法不可行,同(6);
(12)storyboard生成ViewController并添加按钮+storyboard中添加导航栏+使用pushViewController跳转到另一个xib生成的ViewController;-->正常,可以进行跳转,并可以使用导航栏自带的返回按钮返回上一个页面;同(5);
(13)xib生成ViewController并添加按钮+代码生成导航栏(重点)+使用pushViewController跳转到另一个xib生成的ViewController;-->正常,可以进行跳转,并可以使用导航栏自带的返回按钮返回上一个页面;同(5);
(14)xib生成ViewController并添加按钮+代码生成导航栏(重点)+使用presentViewController跳转到另一个xib生成的ViewController;-->跳转正常,但是第二个界面不会出现导航栏,该方法不可行,同(6);
可能看到上面的东西,大家已经晕了。记忆上面的内容是没有任何意义的。我下面通过一些实例的讲解,由浅入深,大家就可以自己通过来验证上面的功能。
【modal跳转,没有导航栏】
通过两行代码来实现modal的跳转:
- //要跳转到下一个界面的控制器;
- SecondViewController *second = [[SecondViewController alloc] init];
- [self presentViewController:second animated:true completion:nil];
【push跳转,有导航栏】
- //要跳转到下一个界面的控制器;
- SecondViewController *second = [[SecondViewController alloc] init];
- [self.navigationController pushViewController:second animated:true];
【从nib文件启动,并代码实现导航栏】
当我们新建一个iOS的Single View Application的时候,程序是默认从storyboard中的某个界面开始启动的。如果我们的应用想彻底抛弃storyboard,那么应该怎么来设置第一个显示的界面呢?实现方法如下:
(1)删除storyboard,并在Targets-->General-->Deployment Info-->Main Interface中删除Main,否则程序会去找这个Main.storyboard。
(2)在AppDelegate.h中新增一个属性:
- #import <UIKit/UIKit.h>
- @class MainViewController;
- @interface AppDelegate : UIResponder <UIApplicationDelegate>
- @property (strong, nonatomic) UIWindow *window;
- //定义全局可用的导航栏;
- @property(nonatomic,strong) UINavigationController *naviController;
- @end
(3)在AppDelegate.m中实现如下:
- #import "AppDelegate.h"
- #import "MainViewController.h"
- @interface AppDelegate ()
- @end
- @implementation AppDelegate
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
- //注意这里非常重要,我的程序完全没有使用storyboard,包括启动后的第一个界面也是通过xib来加载的。
- /*下面的方法有两个作用:
- (1)使第一个界面为MainViewController,并且该界面是用xib实现的;
- (2)增加导航栏;
- */
- //设置整个界面的大小;
- self.window=[[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
- //设置根控制器为MainViewController;
- self.window.rootViewController=[[MainViewController alloc]init];
- //把导航栏设置到根控制器;
- self.naviController = [[UINavigationController alloc] initWithRootViewController:self.window.rootViewController];
- //把控制器加入到界面中;
- [self.window addSubview:self.naviController.view];
- //使界面可见;
- [self.window makeKeyAndVisible];
- return YES;
- }
- @end
(4)从MainViewController点击按钮跳转到SecondViewController界面,使用push进行跳转。注意这里我的MainViewController和SecondViewController的界面都是用nib文件来构建的。是在创建ViewController的同时Also create XIB file的。
- - (IBAction)buttonPressed:(id)sender {
- SecondViewController *second = [[SecondViewController alloc] init];
- [self.navigationController pushViewController:second animated:true];
- }
【界面返回方法汇总】
这里我将会主要设计三种界面返回方式:
1.dismissViewControllerAnimation:这是Modal跳转方式后的返回;
2.popViewControllerAnimation:这是Push跳转方式后的返回;
3.导航栏自带的返回按钮,效果同popViewControllerAnimation。
结合上面的14条界面跳转时的情况,我总结了6条界面返回时的情况:
- (1)xib文件定义第一个ViewController+使用代码生成导航栏+使用pushViewController进行跳转+使用导航栏默认返回键返回-->能够正常进行返回,提倡这种方式;
- (2)xib文件定义第一个ViewController+使用代码生成导航栏+使用pushViewController进行跳转+使用某按钮点击执行[[self navigationController] popViewControllerAnimated:true]返回-->能够正常进行返回,提倡这种方式,同(1);
- (3)xib文件定义第一个ViewController+使用代码生成导航栏+使用presentViewController进行跳转+使用dismissViewControllerAnimated进行返回-->可以正常进行跳转与返回,但会导致第一个界面有导航栏,第二个界面没有导航栏,不提倡这种方式;
- (4)xib文件定义第一个ViewController+使用presentViewController进行跳转+使用dismissViewControllerAnimated进行返回-->能够正常进行跳转返回,modal视图下提倡这种方式;
- (5)xib文件定义第一个ViewController+使用代码生成导航栏+使用pushViewController进行跳转+使用dismissViewControllerAnimated进行返回-->无法进行正常的返回;
- (6)xib文件定义第一个ViewController+使用代码生成导航栏+使用presentViewController进行跳转+使用[[self navigationController] popViewControllerAnimated:true]进行返回-->无法进行正常的返回;
个人总结的一句话就是:如果使用了导航栏,就配对使用push和pop进行跳转返回; 如果没有使用导航栏,就配对使用present和dismiss进行跳转返回;
【dismiss返回代码】
- [self dismissViewControllerAnimated:true completion:nil];
【pop返回代码】
- [[self navigationController] popViewControllerAnimated:true];
【Modal跳转界面样式】
Modal视图跳转的时候,默认界面是从从下往上出现的。这样其实非常的单调。苹果也给我们提供了几种不同的样式。并且注意,storyboard中的界面如果没有使用segue进行跳转的话,跳转代码中要使用
instantiateViewControllerWithIdentifier这个方法来进行绑定。
- - (IBAction)buttonpressed:(id)sender {
- UIViewController *second = [[UIViewController alloc ]init];
- second = [self.storyboard instantiateViewControllerWithIdentifier:@"SecondViewController"];
- second.modalTransitionStyle = UIModalTransitionStylePartialCurl;
- [self presentViewController:second animated:true completion:nil];
- }
其中modalTransitionStyle共有四种:
UIModalTransitionStyleCoverVertical:从下往上弹出界面;
UIModalTransitionStyleFlipHorizontal:前后翻转翻页;
UIModalTransitionStyleCrossDissolve:淡入淡出效果;
UIModalTransitionStylePartialCurl:书本翻页效果;
【Push跳转界面样式】
Push进行界面跳转的时候默认是从右向左进行切换的。我们也有办法进行Style的切换。实现代码如下:
- - (IBAction)buttonPressed:(id)sender {
- //push时的跳转动画;
- CATransition *animation = [CATransition animation];
- [animation setDuration:1];
- //过渡类型;
- [animation setType: kCATransitionMoveIn];
- //这个是设置界面进入的方向;
- [animation setSubtype: kCATransitionFromBottom];
- //界面进入的时间函数;
- [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
- SecondViewController *second = [[SecondViewController alloc] init];
- //加载storyboard中的ViewController;
- second = [self.storyboard instantiateViewControllerWithIdentifier:@"SecondViewController"];
- [self.navigationController pushViewController:second animated:false];
- [self.navigationController.view.layer addAnimation:animation forKey:nil];
- }
可以设定的参数如下:
Type:
kCATransitionFade ;kCATransitionMoveIn ;kCATransitionPush ;kCATransitionReveal ;
SubType:
kCATransitionFromRight;kCATransitionFromLeft;kCATransitionFromTop;kCATransitionFromBottom;
TimingFunction:
kCAMediaTimingFunctionLinear;kCAMediaTimingFunctionEaseIn;kCAMediaTimingFunctionEaseOut;kCAMediaTimingFunctionEaseInEaseOut;kCAMediaTimingFunctionDefault;
以上的参数可以可以相互组合,就会有不同的效果。
【ViewController加载nib文件】
当我们通过New Files-->Source-->Cocoa Touch Class-->生成一个ViewController后,假设并没有勾选Also create XIB file, 然后我们再通过New files-->User Interface-->View建立一个nib文件后,如何把两样东西绑定起来?现在我通过最简单的方式来进行绑定。
(1)选中nib文件的File's Owner,在右侧的Class位置中输入ViewController的名字,如SecondViewController。如图:
(2)右键File's Owner,拖拽至View,会弹出Outlets,选中View即可。
。
(3)通过以上两个步骤,就实现了代码与nib的绑定。与创建ViewController是同时创建nib无异。
【push后的界面返回】
下面使用到的返回都是之前已经在导航栏栈中的,返回的界面会保存上一次push后的状态,而不是到一个全新的界面。
(1)使用导航栏默认的返回键进行返回;
(2)使用按钮点击返回,效果同导航栏返回效果,会默认返回上一个界面,方法如下:
- [self.navigationController popViewControllerAnimated:true];
(3)点击按钮直接返回到根视图。如果我们通过AppDelegate中代码来生成导航栏,那么同时会设置rootViewController,这个视图就是根视图。也就是导航栏中的第一个。此时返回到的根视图也会保存上一次的根视图的状态。
- [self.navigationController popToRootViewControllerAnimated:true];
(4)返回到任意一个导航栏中的视图。这里同样使用的是pop的方式,也会保存某个界面的状态。注意这里和push的区别就是push是到一个全新加载的界面,而pop是回到已经加载完成的界面。方法实现如下:
- for (UIViewController *controller in self.navigationController.viewControllers) {
- //首先要找到的是返回的是哪一个ViewController.
- if ([controller isKindOfClass:[FirstViewController class]]) {
- [self.navigationController popToViewController:controller animated:true];
- }
【pop返回的数据传递】
我们在进行push时,往往会传递数据,但是在返回时我们如何进行数据传递呢?我通过代码来演示一下:
- /*
- 界面返回的时候传递数据;
- */
- //遍历导航栏中的所有ViewController;
- for (UIViewController *controller in self.navigationController.viewControllers) {
- //判断该ViewController是否是我们想要的;
- if ([controller isKindOfClass:[FirstViewController class]]) {
- FirstViewController *first = [[FirstViewController alloc] init];
- first = (FirstViewController *)controller;
- //传递数据;
- first.dataFromThird = @"chen";
- [self.navigationController popToViewController:controller animated:true];
- }
- }
另一个界面中的属性应该声明在.h文件中,这样外界才能访问。通过以上代码,我们就可以任意的push和pop到某个界面并传递数据。
总结,就算是一个小小的导航栏和界面跳转,里面也包含了很多的内容,我这里涉及的也只是很小的部分。我们没必要去记忆它们,可以在以后开发实践过程中慢慢掌握即可。
最近开源的iOS应用,高仿印象笔记 https://github.com/chenyufeng1991/iOS-Oncenote 。欢迎大家点赞并关注项目进度。也可以安装到手机上试玩哦。
github主页:https://github.com/chenyufeng1991 。欢迎大家访问!