一、UINavigationController介绍
1、导航控制器继承自UIViewController,本身是一个控制器。
2、导航控制器是一个容器,可以容纳视图控制器,通过入栈和出栈来管理容器内的控制器。在纵向管理视图控制器上是非常重要的。
3、导航控制器内封装了navigationBar,一个导航控制器都有一个导航条。
示例代码:
1、导航控制器相关属性
-(void)showViewController{
//数组:导航栈内所有的视图控制器都在这个数组内
NSArray * viewControllers = self.navigationController.viewControllers;
NSLog(@"viewControllers = %@",viewControllers);
//导航栈内控制器的个数
NSUInteger numViewController = viewControllers.count;
NSLog(@"numViewController = %ld",numViewController);
//当前显示出来的视图控制器
UIViewController * visibleController = [self.navigationController visibleViewController];
NSLog(@"visibleController = %@",visibleController);
//栈顶的视图控制器
UIViewController * topController = [self.navigationController topViewController];
NSLog(@"topController = %@",topController);
}
日志输出如下:
2、导航控制器入栈方法
-(void)goToNext:(UIButton *)btn{
SecondViewController * secondVC = [[SecondViewController alloc] init];
#if 0
//设置navigationcontroller所有的控制器
self.navigationController.viewControllers = @[self,secondVC];
#elif 0
//设置navigationcontroller所有的控制器
NSArray * viewControllers = @[self,secondVC];
[self.navigationController setViewControllers:viewControllers animated:YES];
#elif 1
[self.navigationController pushViewController:secondVC animated:YES];//常用方法
#endif
//导航栈内的控制器个数
NSLog(@"num = %lu",self.navigationController.viewControllers.count);
}
3、导航控制器出栈方法
-(void)goToBack:(UIButton *)btn{
#if 1
//将当前视图控制器弹出栈
[self.navigationController popViewControllerAnimated:YES];
#elif 0
//某个视图控制器之上的控制器都弹出栈
[self.navigationController popToViewController:self.navigationController.viewControllers[0] animated:YES];
#elif 0
//将根视图控制器之上的所有控制器都弹出栈
[self.navigationController popToRootViewControllerAnimated:YES];
#endif
NSLog(@"num = %lu",self.navigationController.viewControllers.count);
}
二、代理方法: < UINavigationControllerDelegate >
//设置代理
self.navigationController.delegate = self;
//将要显示控制器
-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
//PS:
//所有在导航栈内的控制器只要将要显示都会来到这个方法,比如这个方法写在firstViewController这个类内,self.navigationController.delegate = self;当secondViewController推入第三个thirdViewController时也会来到这个方法。
//原因:导航栈在推入一个新的控制器的时候,上个控制器并未销毁,其代理还是存在的。所以会调用代理方法。
}
//控制器显示已经完成
-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
NSLog(@"viewcontroller = %@",viewController);
}
三、自定义控制器
说明:
1、导航控制器在推入下一个控制器的时候,导航条左侧有个系统默认的返回按钮,单击此返回按钮会返回到上一页。
2、同时长按屏幕向左滑动也会回到上一页。
3、如果系统的返回按钮被自定义的按钮覆盖的话,长按屏幕向左滑动的功能就会失效。自定义导航控制器需要注意这个问题。
比如用下面的方法自定义导航条的左侧按钮:
-(void)setLeftBarItem{
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(0, 0, 80, 40);
[btn setTitle:@"返回" forState:UIControlStateNormal];
UIBarButtonItem * leftItem = [[UIBarButtonItem alloc] initWithCustomView:btn];
[btn addTarget:self action:@selector(goToBack:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = leftItem;
}
.h文件
#import <UIKit/UIKit.h>
//继承自UINavigationController
@interface LKLNavigationController : UINavigationController
@end
.m文件
#import "LKLNavigationController.h"
@interface LKLNavigationController ()<UINavigationControllerDelegate>
@property (nonatomic,weak)id popDelegate;
@end
@implementation LKLNavigationController
+(void)initialize{
//在此方法内进行一些默认属性的设置
}
-(instancetype)init{
self = [super init];
if (self) {
//导航条的初始化方法:- (instancetype)initWithRootViewController:(UIViewController *)rootViewController;
//貌似不会调用这个方法,所以一些默认的属性设置写在此方法内是不会执行的。
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
//实现滑动功能
[self slideToPre];
}
//重写父类的push..方法
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
//这个数组里放置了所有被push到导航栈里的控制器,并且第一个在最底层,最后一个在最顶层
//需要说明的是:
//在调用父类方法之前,判断导航栈内的控制器个数
//此时在此方法内打印出的是0,如下代码。
NSArray * controllers1 = self.viewControllers;
NSLog(@"LKLNavigatinController.ViewControllers.count = %lu",controllers1.count);// 0
if (self.viewControllers.count == 0) {
//此时证明下面将要push的是根控制器
}
else{
//下面推入的不是根控制器时,自定义导航条左右两边的按钮
//存在的问题:覆盖系统的导航条返回按钮滑动功能失效
//左边
viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(backToPre)];
//右边
viewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(backToRoot)];
viewController.hidesBottomBarWhenPushed = YES;
}
//新控制器入栈
[super pushViewController:viewController animated:animated];
//在调用父类方法之后,判断导航栈内的控制器个数
//此时在此方法内打印出的是1,如下代码。
NSArray * controllers2 = self.viewControllers;
NSLog(@"RRRLKLNavigatinController.ViewControllers.count = %lu",controllers2.count);
if (self.viewControllers.count == 1) {
//此时证明上面刚push进来的是根控制器
}
}
#pragma mark - 点击导航按钮时调用的方法
-(void)backToPre{
[self popViewControllerAnimated:YES];
}
-(void)backToRoot{
[self popToRootViewControllerAnimated:YES];
}
#pragma mark - 实现滑动返回功能
-(void)slideToPre{
#if 0
//清空滑动返回手势的代理,就能实现滑动返回,
//首页没有自定义导航条的左右按钮,所以不应该清除。所以清除代理应区分情况,下面的代码放在这里就不合适
self.interactivePopGestureRecognizer.delegate = nil;
#else
//既然不能在此清除,那么久先记录下手势代理
//在导航控制器的代理方法中选择时机清除代理,
_popDelegate = self.interactivePopGestureRecognizer.delegate;
//遵守<UINavigationControllerDelegate>让自己成为自己的代理
self.delegate = self;
#endif
}
#pragma mark - <UINavigationControllerDelegate>
#pragma mark - 导航控制器的代理方法
//只要控制器发生改变就会调用这两个方法
-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
}
-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
if (viewController == self.viewControllers[0]) {
//根控制器没有覆盖系统的导航条按钮 最好不要修改系统的代理 否则可能产生问题
self.interactivePopGestureRecognizer.delegate = _popDelegate;
}
else{
self.interactivePopGestureRecognizer.delegate = nil;
}
}
@end