ios 跳转指定页面和插入页面

 对于iOS开发来说,Navigation和TabBar是最常用到的界面元素。我在前面几篇博客中《iOS开发——代码生成TabBar与视图切换详解》《iOS纯代码实现界面建立、跳转、导航栏(无storyboard、无nib)》《iOS开发——界面跳转与返回及视图类型详解》中较为详细的实现了导航栏和TabBar的实现,以及一些界面跳转的实现。但是,在实际开发中,越来越发现Navigation和TabBar非常的博大精深。今天我们继续较为深入和全面的对需要Navigation和TabBar的项目机型基础的架构。并对界面栈的重构做一个深入的了解。

     个人建议,在目前的iOS开发现状下,还是推荐使用xib来构建界面,纯代码和storyboard还不是十分推荐(个人看法)。所以在该篇博客的实现中,我使用xib来实现代码。项目源代码上传至 https://github.com/chenyufeng1991/MoreNavigation 。里面已经有一些代码注释,对于理解还是比较有用的。

(1)Navigation+Tab构建项目

目前市面上大多数的iOS App,都是使用Navigation+Tab的方式来构建,这基本上是一种主流趋势。因为有了这两个工具,可以方便的对程序的流程和页面进行控制,整体的逻辑结构也会变得清晰。我的项目主要使用xib来构建项目,已经删除了自带的Main.storyboard。

在AppDelegate.h中声明一个TabBar属性:

[objc]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. @property (strongnonatomicUITabBarController *tabController;  

在AppDelegate.m中分别构造Navigation和Tab。我这里的思路是:把TabBar作为容器,有三个Tab页,每个Tab页分别管理自己的Navigation,然后把这个TabBar作为window的根视图。最后让window显示即可。

[objc]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  
  2. {  
  3.     //如果本身默认使用storyboard,就不需要window的初始化  
  4.     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];  
  5.     self.window.backgroundColor = [UIColor whiteColor];  
  6.   
  7.     self.tabController = [[UITabBarController alloc] init];  
  8.   
  9.     FirstViewController *first = [[FirstViewController alloc] init];  
  10.     first.title = @"第一页";  
  11.     SecondViewController *second = [[SecondViewController alloc] init];  
  12.     second.title = @"第二页";  
  13.     ThirdViewController *third = [[ThirdViewController alloc] init];  
  14.     third.title = @"第三页";  
  15.   
  16.     //分别管理三个不同的栈,其实就是三个不同的Navigation  
  17.     UINavigationController *naviFirst = [[UINavigationController alloc] initWithRootViewController:first];  
  18.     UINavigationController *naviSecond = [[UINavigationController alloc] initWithRootViewController:second];  
  19.     UINavigationController *naviThird = [[UINavigationController alloc] initWithRootViewController:third];  
  20.   
  21.     self.tabController.viewControllers = @[naviFirst,naviSecond,naviThird];  
  22.     self.window.rootViewController = self.tabController;  
  23.   
  24.     //设置为主window并显示,否则启动后是黑屏  
  25.     [self.window makeKeyAndVisible];  
  26.   
  27.     return YES;  
  28. }  

注意:window一定要使用makeKeyAndVisible来进行显示,否则App启动后是黑屏。


(2)Navigation和TabBar的显示与隐藏设置

当我经过(1)中的执行后,会发现App中的每个页面都有Navigation和Tab,有时候这并不符合我们的要求,在某些页面需要隐藏Navigation或者Tab,可以进行如下设置。


某个页面要隐藏导航栏:

[objc]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. - (void)viewWillAppear:(BOOL)animated  
  2. {  
  3.     [super viewWillAppear:animated];  
  4.     //主界面隐藏导航栏,推荐使用这种方式来设置  
  5.     [self.navigationController setNavigationBarHidden:YES animated:animated];  
  6.   
  7.     //打印此时的界面栈  
  8.     [GlobalKit viewControllersArray:self];  
  9. }  
  10.   
  11. - (void)viewWillDisappear:(BOOL)animated  
  12. {  
  13.     [super viewWillDisappear:animated];  
  14.     [self.navigationController setNavigationBarHidden:NO animated:animated];  
  15. }  

上面的设置最好在viewWillAppear和viewWillDisappear中配对进行设置。否则会有其他的一些小问题。


隐藏某个页面的TabBar:

[objc]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. FourthViewController *vc = [[FourthViewController alloc] init];  
  2.  //进入二级界面,隐藏TabBar  
  3.  vc.hidesBottomBarWhenPushed = YES;  
  4.  [self.navigationController pushViewController:vc animated:YES];  
有时候在pop的时候需要显示或者隐藏TabBar,也可以通过hidesBottomBarWhenPushed设置。

(3)pop到界面栈中存在的某个界面

对于iOS中的界面栈,我们需要有一个明确的认识。界面不断push的过程,其实就是界面不断入栈的过程,当前显示的界面,就是栈顶界面。界面pop的过程,就是界面不断出栈的过程,出栈后的界面也就不可见了。可见的永远都是栈顶界面。系统默认的返回键或者pop方法都是返回到上一个界面。但是在实际开发中,我们有这样的需求,我们需要pop到隔着好几个界面的前面某个界面,那么应该怎么做呢?

[objc]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. //判断前面有没有SecondViewController,有的话pop到SecondViewController页面,否则不进行跳转  
  2. for (UIViewController *vc in self.navigationController.viewControllers)  
  3. {  
  4.     if ([vc isKindOfClass:[SecondViewController class]])  
  5.     {  
  6.         [self.navigationController popToViewController:vc animated:YES];  
  7.         return;  
  8.     }  
  9. }  

以上代码的意思是当前界面栈中是否有SecondViewController,如果有的话,就会pop到SecondViewController,假设当前界面为A界面,那么A界面和SecondViewController之间的所有界面都会出栈。SecondViewController界面就会在栈顶。所以这就是for循环遍历的过程。


(4)重构界面栈,pop到之前不存在的界面

为什么要重构界面栈?在实际开发中会遇到这样的情况,在某几个界面间是一个死循环,在进行跳转的时候不断在几个界面间push,然后同样一个界面就会在栈中出现很多次,虽然这些栈都是不同的实例,但是这样完全没有必要。栈中界面越来越多,难道不会影响性能吗?并且默认有这样的准则:一个VC在栈中只有一个。所以,在必要时候,我们不要老是push,如果一个界面已经出现过,我们可以尝试pop。甚至没有出现过,我们可以在栈中插入一个界面,然后pop到该界面。这是为了从性能上去考虑,我们要不断维护界面栈。界面栈其实就是一个数组,插入删除操作非常方便,但是同样要提高警惕,容易造成crash。

[objc]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  *  这里的需求是,判断我的界面栈前面有没有FirstViewController,如果有的话,在FirstViewController后面插入一个InsertViewController,然后从当前界面pop到InsertViewController,InsertViewController可以pop到FirstViewController。 
  3.  否则,点击按钮不执行任何操作 
  4.  * 
  5.  */  
  6. - (IBAction)insertButtonClicked:(id)sender {  
  7.     NSMutableArray *pageArray = [self.navigationController.viewControllers mutableCopy];  
  8.     for (int i = 0; i < pageArray.count; i++)  
  9.     {  
  10.         id vc = pageArray[i];  
  11.         //找到要插入页面的前一个界面,这里就是FirstViewController  
  12.         if ([vc isKindOfClass:[FirstViewController class]])  
  13.         {  
  14.             InsertViewController *insert = [[InsertViewController alloc] init];  
  15.             //插入界面栈  
  16.             [pageArray insertObject:insert atIndex:i + 1];  
  17.             [self.navigationController setViewControllers:pageArray animated:NO];  
  18.   
  19.             //打印出当前的界面栈  
  20.             [GlobalKit viewControllersArray:self];  
  21.   
  22.             insert.hidesBottomBarWhenPushed = YES;  
  23.             [self.navigationController popToViewController:insert animated:YES];  
  24.             return;  
  25.         }  
  26.     }  
  27. }  

(5)获取界面栈

界面栈其实是由Navigation来获取的。每一次push就是进栈,每一次pop就是出栈。使用self.navigationController.viewControllers即可。我在GlobalKit中已经封装了方法。需要在查看界面栈的地方调用该方法即可,然后就可以打印该数组了。

[objc]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. //打印当前界面的栈,使用方便  
  2. + (void)viewControllersArray:(UIViewController *)viewController  
  3. {  
  4.     NSArray *vcArray = viewController.navigationController.viewControllers;  
  5.     NSLog(@"\n%@---%@",viewController,vcArray);  
  6. }  

猜你喜欢

转载自blog.csdn.net/wenhaiwang/article/details/52233486