一、概述
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
第二个控制器:TwoViewControllers
第三个控制器: 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 第一个控制器并不会被销毁
② 当子控制器向上跳转时,期间的子控制器包括自己都会被销毁。