关于UINavigationControllerDelegate:
- Delegate中一共有6个方法。其中两个跟控制器ViewController的跳转有关、有两个跟屏幕的旋转有关、有两个跟导航栏动画有关(可以设计交互式或者非交互式的转场动画)。
前提配置:为了下面所说的测试都能如期的进行,有几个步骤是需要设置好的
1、声明UINavigationControllerDelegate协议
@interface ViewControllerOne () <UINavigationControllerDelegate>
2、遵守该协议
self.navigationController.delegate = self;
3、在遵守该协议的控制器中写出需要用到的协议方法
一、首先介绍下跟控制器的跳转相关的两个协议方法:
// Called when the navigation controller shows a new top view controller via a push, pop or setting of the view controller stack. - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { NSLog(@"~~~~~~~~~~willShowViewController"); } - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { NSLog(@"~~~~~~~~~~didShowViewController"); }
说明:
1、当控制器push、pop、直接设置navigationController的controllers属性,都是可以触发这两个方法的。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { ViewControllerTwo *vcTwo = [[ViewControllerTwo alloc] init]; // 可触发 [self.navigationController pushViewController:vcTwo animated:YES]; /* 不可触发 [self.view addSubview:vcTwo.view]; [self addChildViewController:vcTwo]; [vcTwo didMoveToParentViewController:self]; */ /* 不可触发 [self presentViewController:vcTwo animated:YES completion:nil]; */ /* 可触发 self.navigationController.viewControllers = @[self, vcTwo]; */ }
2、为了能够准确的理解这两个方法,一定要记住这个是navigationController的协议方法。对它来说,即将进入这个控制器栈时调用willShow方法,已经进栈后调用didShow方法。
当vc1-push-vc2时,是vc2willShow、vc2didShow,
当vc2-pop-vc1时,是vc1willShow、vc1didShow。
3、因为navigationController自身、凡是进栈后的控制器都能够拿到navigationController这个对象,因此都可以成为它的代理。但是,有两个原则:
(1)始终以最新成为代理的为准。
(2)当最新成为代理的控制器销毁后(因为代理是weak,所以不会强留代理),第二新的代理成为接班者。
但是有个注意点:一个控制器销毁是在另一个控制器didShow之后,一个控制器成为接班者是在didShow之后。
举例如下:vc1、vc2都是navigationController的代理。过程是vc1-push-vc2,然后vc2-pop-vc1。
vc1-push-vc2的过程是:vc1是真代理、vc2willShow、vc2didshow、vc2成为真代理。
vc2-pop-vc1的过程是:vc2是真代理、vc1willShow、vc1didShow、vc1成为真代理。
二、然后介绍下跟屏幕的旋转有关的代理(真的不常用,而且真的比较坑)
首先要说明白的是,屏幕旋转有三个相关的地方:手机上的旋转开关、项目在Xcode上的配置、接下来要介绍的这2个协议方法。
// 返回-导航控制器支持的设备方向 // 每次旋转设备的时候询问(前提是iPhone上的屏幕旋转开关是开着的) - (UIInterfaceOrientationMask)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController { NSLog(@"~~~~~设置方法设置导航控制器支持的设备方向~~~~~"); return UIInterfaceOrientationMaskAllButUpsideDown; } // 这个方法设置导航控制器的首选设备方向 - (UIInterfaceOrientation)navigationControllerPreferredInterfaceOrientationForPresentation:(UINavigationController *)navigationController { NSLog(@"~~~~~这个方法设置导航控制器的首选设备方向~~~~~"); return UIInterfaceOrientationLandscapeLeft; }
现在表述下手机上的旋转开关、Xcode上的配置、协议方法、屏幕的方向四者之间的联系:
(1)当手机上的旋转开关关闭时,Xcode上的配置失效、当设备旋转时协议方法不调用、屏幕只能是竖屏显示。
(2)当手机上的旋转开关开着时,屏幕的可旋转方向由「Xcode上的配置」和「可旋转方向的协议方法」两者的交集决定。当设备旋转时,「可旋转方向的协议方法」被调用,屏幕会旋转成交集中存在的方向。
(3)至于屏幕最初显示的方向的依据现象是这样的:只要「Xcode上的配置」,包含Portrait方向(即竖屏),则最初显示的方向都是竖屏。只要「Xcode上的配置」不包含Portrait(即竖屏),则最初显示的方向就是设备往左(即屏幕往右)的方向(可以通过下面图像进行理解)。至于「首选设备方向的协议方法」一直不调用。在这种情况下,无关手机上的旋转开关的状态(不管开关,前面文字描述的内容依然成立)。
吐槽:
(1)虽然Xcode上的配置写的是“Device Orientation”,但是真实功能却是“可选方向”的作用。并且真实的可选方向,还必须是「Xcode上的配置」和「可旋转方向协议方法」两者的交集。
(2)「首选设备方向的协议方法」一直不被调用。
三、简介下跟导航栏转场动画相关的两个协议方法
// 没有交互的转场动画 - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { NSLog(@"~~~~~~~~~~%ld~%@~%@", operation, fromVC.class, toVC.class); return nil; } // 有交互的转场动画 - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController { NSLog(@"~~~~~~~~~~%@", animationController); return nil; }
免责说明:
关于转场动画,不在该篇文章的讨论范围。其实也是因为内容比较多,后面在专门重起一文再议。
简单闲扯下:
1、转场动画可以分为交互式与非交互式,如果设置非交互式的用上面第一个协议方法就可以了。
2、第一个协议方法中的operation参数,可以知道当前进行的是push还是pop操作。