IOS视图控制器导航及生命周期研究Demo

1、背景:

2014年4月份第一次接触IOS端开发,为某银行开发一款金融app。
在开发的最后阶段,加入了需要从任意一个页面直接返回主页的功能。
悲催的是,当时没有使用UINavigationController进行导航管理,而是使用了IOS中的模态跳转方式(presentViewController/dismissViewControllerAnimated).

因此需要找的一种方法进行,实现如下方式的导航跳转:

已知: 页面a→页面b→页面c

求解: 页面c直接跳回到页面a,并且要求不能有内存泄露,循环依赖等

2、解题思考:

面对上面的需求,最简单的方式是将所有控制器都改成UINavigationController,并且利用pushViewController / popToViewController/ popToRootViewControllerAnimated 等方法进行完美解题。但是当时项目的页面将近100个,分成三大模块,需要大规模修改设计页面以及调整大量代码,这并不是一个现实的解决方案,不到万不得已,不能采取如此低劣手段!

我们需要一个满足如下条件的解决方案:

1) 对于已经在InterfaceBuilder中完成的页面,不做任何修改
2) 尽量少的修改代码,因为很多代码已经经过测试中心测试过,如果修改,需要全部重新测试,时间来不及。

当时通过两天的研究,深入的了解了IOS中的跳转流程和生命周期后,找到了一个相对完美的解决方案,能够满足上面提到的要求。通过一个Demo,来和大家一起分享。

3、解题流程:

1) 为了减少代码的修改,增加一个基类。
@interface BaseViewController : UIViewController

//防止controller循环引用,使用weak引用方式
@property(nonatomic,weak) BaseViewController* parentController;

//为了能够了解某个页面控制器生命周期相关信息,给该控制器取个名字
@property(nonatomic,copy)   NSString*   ctrlName;

//关键的函数,进行页面c-->跳转到主页
-(void) doDismiss;

@end
2) 为了更好的了解IOS中ViewController的生命周期,我们在基类中输出相关信息来了解生命周期相关信息。
@implementation BaseViewController

-(id) init
{
    self.ctrlName = @"";
    self.parentController = nil;
    return [super init];
}

- (void)viewDidLoad
{
    NSLog(@"%@ invoke viewDidLoad",self.ctrlName);
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

-(void)viewWillAppear:(BOOL)animated
{
    NSLog(@"%@ invoke viewWillAppear",self.ctrlName);
    [super viewWillAppear:animated];
}

-(void)viewDidAppear:(BOOL)animated
{
    NSLog(@"%@ invoke viewDidAppear",self.ctrlName);
    [super viewDidAppear:animated];
}

-(void) viewWillDisappear:(BOOL)animated
{
    NSLog(@"%@ invoke viewWillDisappear",self.ctrlName);
    [super viewWillDisappear:animated];
}

-(void) viewDidDisappear:(BOOL)animated
{
    NSLog(@"%@ invoke viewDidDisappear",self.ctrlName);
    [super viewDidDisappear:animated];
}

- (void)didReceiveMemoryWarning
{
    NSLog(@"%@ invoke didReceiveMemoryWarning",self.ctrlName);
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)dealloc
{
    NSLog(@"%@ .......dealloc.......",self.ctrlName);
}
3) 关键的递归函数,核心是理解dismissViewControllerAnimated:completion函数中completion回调的时机点,这个是解题的钥匙。
-(void)doDismiss
{
    NSLog(@"%@ dismiss begin",self.ctrlName);

    if([self.parentController isKindOfClass:[ViewController class]])
    {
        [self dismissViewControllerAnimated:YES completion: ^(void)
         {
             NSLog(@"%@ dismiss end",self.ctrlName);

             if(self.parentController)
             {
                 [self.parentController doDismiss];
             }
         }];
    }
    else
    {
        [self dismissViewControllerAnimated:NO completion: ^(void)
         {
             NSLog(@"%@ dismiss end",self.ctrlName);

             if(self.parentController)
             {
                 [self.parentController doDismiss];
             }
         }];

    }
}
4) Demo页面结构如下图: MainViewController-->SecondViewController-->Child1ViewController,然后直接跳回到MainViewController.

5) 所有的ViewController都继承自我们自定义的BaseViewController,具有关键的doDismiss递归方法!

6) 看一下从MainViewController-->SecondViewController-->Child1ViewController-->MainViewController生命周期的相关信息:

a、启动程序,进入MainViewController:
2017-02-11 17:25:16.964 navTest[933:17086] mainViewCtrl invoke viewDidLoad
2017-02-11 17:25:16.966 navTest[933:17086] mainViewCtrl invoke viewWillAppear
2017-02-11 17:25:16.975 navTest[933:17086] mainViewCtrl invoke viewDidAppear

b、从MainViewController→SecondViewController:
2017-02-11 17:28:29.717 navTest[933:17086] SecondViewController invoke viewDidLoad [先newController调用viewDidLoad]
2017-02-11 17:28:29.721 navTest[933:17086] mainViewCtrl invoke viewWillDisappear [然后oldController调用viewWillDisappear]
2017-02-11 17:28:29.722 navTest[933:17086] SecondViewController invoke viewWillAppear [然后newController调用viewWillAppear]
2017-02-11 17:28:30.229 navTest[933:17086] SecondViewController invoke viewDidAppear [然后newController调用viewDidAppear ]
2017-02-11 17:28:30.229 navTest[933:17086] mainViewCtrl invoke viewDidDisappear [然后oldController调用viewDidDisappear ]
2017-02-11 17:28:30.230 navTest[933:17086] mainViewCtrl present end
[最后oldController调用presentViewController完成回调被调用]

下面是presentViewController完成回调信息的输出代码,用来了解completion是在哪个阶段被调用的,很重要的信息哦!!!

- (IBAction)ClickToEnterSecondController:(id)sender {
    SecondViewController* ctrl = [self.storyboard instantiateViewControllerWithIdentifier:@"second"];
    ctrl.parentController = self;
    ctrl.ctrlName = @"SecondViewController";
    [self presentViewController:ctrl animated:YES completion:^(void)
     {
         NSLog(@"%@ present end",self.ctrlName);
     }];
}

c、从Child1ViewController直接返回到MainViewController的流程(跳过SecondViewController)

- (IBAction)returnMainViewController:(id)sender {
    NSLog(@"**********************doDismiss*********************");
    [self doDismiss];
}

d、2017-02-11 17:30:30.634 navTest[933:17086] **doDismiss* [要开始调用doDismiss函数了,表示点击了返回按钮]
2017-02-11 17:30:30.635 navTest[933:17086] Child1ViewController dismiss begin  [输出dismiss begin,表示调用了Child1ViewController的doDismiss递归函数]
2017-02-11 17:30:30.637 navTest[933:17086] Child1ViewController invoke viewWillDisappear [oldController viewWillDisappear]
2017-02-11 17:30:30.638 navTest[933:17086] SecondViewController invoke viewWillAppear [newController viewWillAppear]
2017-02-11 17:30:30.641 navTest[933:17086] SecondViewController invoke viewDidAppear [newController viewDidAppear]
2017-02-11 17:30:30.641 navTest[933:17086] Child1ViewController invoke viewDidDisappear [oldController viewDidDisappear ]
2017-02-11 17:30:30.641 navTest[933:17086] Child1ViewController dismiss end [关键时刻哦,oldController self dismissViewControllerAnimated: completion: 中的completion 回调被触发了,它是在 oldController viewDidDisappear后被触发的哦]
2017-02-11 17:30:30.641 navTest[933:17086] SecondViewController dismiss begin [这时候,从ChildViewController回弹到SecondViewController的流程完成了,接下来递归调用,要完成从SecondViewController回弹到MainViewController的过程,重复上面的流程而已]
2017-02-11 17:30:30.642 navTest[933:17086] Child1ViewController …….dealloc……. [Child1ViewController已经完全消失了,因此内存被析构了,这样确保内存不会泄露哦]

下面是递归部分,和上面流程一样,只是从SecondViewController回跳到MainViewController

2017-02-11 17:30:30.643 navTest[933:17086] SecondViewController invoke viewWillDisappear
2017-02-11 17:30:30.644 navTest[933:17086] mainViewCtrl invoke viewWillAppear
2017-02-11 17:30:31.156 navTest[933:17086] mainViewCtrl invoke viewDidAppear
2017-02-11 17:30:31.156 navTest[933:17086] SecondViewController invoke viewDidDisappear
2017-02-11 17:30:31.156 navTest[933:17086] SecondViewController dismiss end
2017-02-11 17:30:31.157 navTest[933:17086] mainViewCtrl dismiss begin
2017-02-11 17:30:31.157 navTest[933:17086] SecondViewController …….dealloc…….  [SecondViewController 内存也很完美的被析构掉,目前仅剩下MainViewController还活着]

show一下某银行APP以前模拟器中的效果截图,IOS开发还是非常令人非常愉悦的感觉!

源码下载:

IOS UIViewController 导航及生命周期Demo

时间: 2024-12-16 20:36:24

IOS视图控制器导航及生命周期研究Demo的相关文章

iOS开发UI篇—UITableBarController生命周期(使用storyoard搭建)

iOS开发UI篇-UITabBarController生命周期(使用storyoard搭建) 一.UITabBarController在storyoard中得搭建 1.新建一个项目,把storyboard中默认的控制器删除,拖UITab Bar Controller. 2.创建viewcontroller,添加到UITab Bar Controller中去(连线). 注意点:连线的顺序就是将来显示的顺序,显示在眼前的为第一个连线的view. 提示:控制器的界面对应的tabbarbutton和图片

IOS视图控制器的生命周期

原创Blog,转载请注明出处 http://blog.csdn.net/hello_hwc?viewmode=contents 所谓的生命周期,也就是几个函数的调用顺序,这里以用Storyboard来创建一个ViewController为例 然后我们测试如下代码 // // ViewController.m // // Created by huangwenchen on 14/12/26. // Copyright (c) 2014年 huangwenchen. All rights rese

iOS 控制器View的生命周期及相关函数

1.loadView 1.1 如果重写了控制器的loadView方法,则控制器的View按照loadView方法的描述去创建 - (void)loadView { self.view = [[UIView alloc]init]; self.view.backgroundColor = [UIColor redColor]; } 1.2 如果没重写控制器的loadView方法,则看有没有storyboard的,有的话,则按storyboard的描述创建view 加载storyboard的方法:

创建控制器、控制器加载view过程、控制器view的生命周期、多控制器

在介绍四大对象的那篇博客中,可以基本了解到程序启动的过程: main-->UIApplicationMain-->创建UIApplication的实例和app代理AppDelegate的实例并设置好代理--->在程序启动后,也就是启动画面显示之后, AppDelegate创建UIWindow(可以是自动创建的,也可以手动创建) 现在讨论的问题是,如何创建控制器并设置为UIWindow的根控制器,然后加载出控制器中的view并显示出来. 本文目录 1.创建控制器的三种方式 2.控制器的vi

ios – 视图控制器如何管理视图?

移动设备的屏幕有限,所有的东西都需要放到一个单一窗口组成的单一界面显示,在ios中体现为视图切换(在<ios – 视图>中已经说明了视图),当一个视图替换掉另一个视图的时候,会经常使用动画效果,这个任务就是交给视图管理器来完成的. ios5之后应用程序窗口有一个根视图控制器(rootViewController),当不为rootViewController赋值时,会出现"Application windows are expected to have a root view cont

iOS view和viewController的生命周期

转自:http://blog.sina.com.cn/s/blog_801997310101a39w.html 一.ViewController的职责 对内管理与之关联的View,对外跟其他ViewController通信和协调.对于与之关联的View,ViewController总是在需要的时候才加载视图,并在不需要的时候卸载视图,所以也同时担当了管理应用资源的责任 二.ViewController的生命周期 View是指Controller的View.它作为Controler的属性,生命周期

iOS多线程全套:线程生命周期,多线程的四种解决方案,线程安全问题,GCD的使用,NSOperation的使用

目的 本文主要是分享iOS多线程的相关内容,为了更系统的讲解,将分为以下7个方面来展开描述. 多线程的基本概念 线程的状态与生命周期 多线程的四种解决方案:pthread,NSThread,GCD,NSOperation 线程安全问题 NSThread的使用 GCD的理解与使用 NSOperation的理解与使用 Demo在这里:WHMultiThreadDemo Demo的运行gif图如下: 一.多线程的基本概念 进程:可以理解成一个运行中的应用程序,是系统进行资源分配和调度的基本单位,是操作

IOS UI-控制器的生命周期

一.控制器的生命周期 代码 1 @interface NJOneViewController () 2 3 @property (nonatomic, strong) NSArray *foods; 4 @end 5 6 @implementation NJOneViewController 7 8 // 当控制器的view加载完毕就调用 9 - (void)viewDidLoad 10 { 11 [super viewDidLoad]; 12 NSLog(@"1控制器的view加载完毕&quo

iOS开发UI篇—UITabBarController生命周期(使用storyoard搭建)

一.UITabBarController在storyoard中得搭建 1.新建一个项目,把storyboard中默认的控制器删除,拖UITab Bar Controller. 2.创建viewcontroller,添加到UITab Bar Controller中去(连线). 注意点:连线的顺序就是将来显示的顺序,显示在眼前的为第一个连线的view. 提示:控制器的界面对应的tabbarbutton和图片显示什么内容,由它的控制器确定. 3.设置子控制器的UITabBar等信息. 4.运行效果 二