导航控制器(NavigationController)的基本属性及生命周期

一、概述
UINavigationController用来管理视图控制器,在多视图控制器中常用。它以栈的形式管理视图控制器,管理视图控制器个数理论上不受限制(实际受内存限制),push和pop方法来弹入弹出控制器,最多只能显示一个视图控制器,那就是处于栈顶的视图控制器。

一般情况下,UINavigationController最少管理一个控制器,即最少有一个根视图控制器或者叫做栈底视图控制器。当然也有例外,如果不给它添加视图控制器也不会报错,界面上也有视图,因为UINavigationController继承自UIViewController,也有自己的view,只不过默认情况下.view.backgroundColor为nil,即透明的。
二、常见属性和方法

- (instancetype)initWithRootViewController:(UIViewController *)rootViewController; 

以一个viewController为栈底,实例化一个navigation viewcontroller

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated; 

展示一个viewcontroller,压栈操作(可以有动画效果)。

- (nullableUIViewController *)popViewControllerAnimated:(BOOL)animated;

结束并隐藏一个viewcontroller(dealloc),出栈操作(可以有动画效果),返回出栈的viewcontroller

- (nullableNSArray<__kindofUIViewController *> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated; 

结束并隐藏 在栈中位于一个特殊的viewcontroller之前所有的viewcontroller,并返回

- (nullableNSArray<__kindofUIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated;

出栈到根viewcontroller

@property(nullable,nonatomic,readonly,strong) UIViewController *topViewController; 

栈顶viewcontroller

@property(nullable,nonatomic,readonly,strong) UIViewController *visibleViewController; 

如果模态viewcontroller存在,返回模态viewcontroller,否则栈顶viewcontroller

@property(nonatomic,copy) NSArray<__kindofUIViewController *> *viewControllers; 

当前栈中的元素

- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated NS_AVAILABLE_IOS(3_0); 

设置栈中的元素,animated=yes,根据当前的栈顶viewcontroller在不在这个viewcontroller数组中,模拟一次push和pop的动画。这就相当于对当前的栈进行了一次整体的刷新

下面的bar属性主要是用来显示顶部的导航栏以及底部的工具栏

@property(nonatomic,getter=isNavigationBarHidden)BOOLnavigationBarHidden;
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated; 
@property(nonatomic,readonly) UINavigationBar *navigationBar; 
@property(nonatomic,getter=isToolbarHidden)BOOLtoolbarHidden NS_AVAILABLE_IOS(3_0) __TVOS_PROHIBITED;
- (void)setToolbarHidden:(BOOL)hidden animated:(BOOL)animated NS_AVAILABLE_IOS(3_0) __TVOS_PROHIBITED; 
@property(null_resettable,nonatomic,readonly) UIToolbar *toolbar NS_AVAILABLE_IOS(3_0) __TVOS_PROHIBITED;// 

delegate,可以在压栈出栈操作中,设置一些动画,以及一些额外的操作
@property(nullable,nonatomic,weak)id<UINavigationControllerDelegate> delegate;

这里需要说一下visibleViewController和topViewController:topViewController永远代表着栈顶元素;visibleViewController代表着当前显示的那个vc,这个vc可能是top vc,也有可能是top vc 展示出来的vc。

UINavigationItem以及其与UINavigationBar的关系:
在前面的简介之中,提到UINavigationController管理着一个UIViewController的栈和一个UINavigationBar。其实,一个UINavigationController的实例对应一个UINavigationBar的实例,而一个UINavigationBar的实例同样管理着一个栈,这个栈中的元素就是UINavigationItem。所以,既然一个navigationController对应一个navigationBar,可以推断到UINavigationController的栈和UINavigationBar的栈也是对应的,而两个栈里元素也是一一对应,也就是一个UIViewController的实例也对应着一个UINavigationItem的实例(如果不对应,系统会报异常情况)。在navigationController进行push和pop的同时,navigationBar也在同时做相应的push和pop 。在官方的API中,在UINavigationController.h文件中会有一个UIViewController的一个category,里面定义着一个UINavigationItem的变量。

三、生命周期
先说明一下控制器生命周期的几个方法

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"view加载完成");
}
- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"view即将出现");
}
- (void)viewDidAppear:(BOOL)animated {
    NSLog(@"view已经出现");
}
- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"one view即将消失");
}
-(void)viewDidDisappear:(BOOL)animated {
    NSLog(@"view已经消失");
}
- (void)dealloc {
    NSLog(@"view已经销毁");
}

导航控制器跳转场景
第一个控制器:OneViewControllers


OneViewControllers 代码

第二个控制器:TwoViewControllers


TwoViewControllers 代码

第三个控制器: ThreeViewControllers
ThreeViewControllers 代码

然后运行:

这里写图片描述

可以看出调用的方法是:viewDidLoad -> viewWillAppear -> viewDidAppear,这个很好理解,接下来:
然后点击跳转到第二个控制器button会怎样呢?
这里写图片描述

可以看出是这样的执行顺序:
oneView的viewWillDisappear -> twoView的viewDidLoad ->twoView的viewWillAppear -> oneView的ViewDidDisAppear ->twoView的ViewDidAppear
它是把第一个控制器先移走,然后装载并且显示第二个控制器,并没有把第一个控制器销毁,而是把它放到了一个内存中的一个位置,是一个弹栈的过程,将第一个控制器弹出。
那么如果点回第一个控制器会怎么样呢?
这里写图片描述

没错,第二个控制器被销毁啦,第一个控制器被压回了栈中,变成了栈顶。为什么要把第二个控制器销毁呢?其实很容易想出来,如果我们一直保留着下一层控制器,那么内存占用肯定会越来越多,但是第一层的控制器你肯定是要回来的,所以没有必要销毁。
那肯定会有人迷惑,如果从第三个控制器直接跳转到第一控制器,第二个控制器会怎样?其实,第二个控制器,也是会销毁的。试验一下:
这里写图片描述

可以看出,在oneView出现之前就把twoView给销毁啦
总结:① 当一个控制器向子控制器跳转时:先执行oneView的viewWillDisappear -> twoView的viewDidLoad ->twoView的viewWillAppear -> oneView的ViewDidDisAppear ->twoView的ViewDidAppear 第一个控制器并不会被销毁

② 当子控制器向上跳转时,期间的子控制器包括自己都会被销毁。

推荐阅读更多精彩内容