1.UINavigationControllerDelegate之push/pop转场动画
在iOS7之后,果爹就提供了可以自定义转场动画。包括navigation的push/pop动画,present/dismiss的动画。定制Push/Pop转场动画,首先需要了解iOS7中提供的几个方法和协议。比较重要的几个协议有: UIViewControllerContextTransitioning;UIViewControllerAnimatedTransitioning
UIViewControllerContextTransitioning通常情况下不需要自己进行实现的,在push/pop中系统会自己创建一个转场的上下文,在上下文可以拿到转场前后的UIViewController,从而拿到转场前后的view;该协议中,存在一个containerView是用来将转场前后的view添加进去来进行具体动画初始frame和最后frame的设置。
UIViewControllerAnimatedTransitioning其实是转场动画的实施者,动画的具体形式,时间都是通过这个协议设置的,自定义的主要内容。
- (nullableid<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController
*)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController
*)fromVC toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
上面这个方法就是UINavigationControllerDelegate的一个协议方法,就是告诉UINavigationController在进行转场时,到底用的是哪个实施者。
2.push/pop转场动画Demo
(1)首先创建一个类实现UIViewControllerAnimatedTransitioning协议,代码如下,第一个方法是设置动画持续的时间;第二个方法才是具体的动画过程:先通过UIViewControllerContextTransitioning的实例获得前后的viewController以及对应的view。然后把后面的viewController加到UIViewControllerContextTransitioning的containerView之上,之后就可以随心所欲的设置动画过程了(没有做不到的动画,只有你想不到的动画)。Demo
中Push和Pop分别是显隐动画和frame的动画。在动画的结束中,需要有[transitionContext completeTransition:YES]的调用,通知转场动画的结束,否则会出现bug。
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.8;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 0.8;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController*
toViewController = [transitionContextviewControllerForKey:UITransitionContextToViewControllerKey];
NSTimeInterval duration = [selftransitionDuration:transitionContext];
UIView* containerView = [transitionContextcontainerView];
switch (self.operation) {
case UINavigationControllerOperationPush:
{
CGRect frame = CGRectMake(0,0,[UIScreenmainScreen].bounds.size.width,[UIScreenmainScreen].bounds.size.height);
NSTimeInterval duration = [selftransitionDuration:transitionContext];
UIView* containerView = [transitionContextcontainerView];
switch (self.operation) {
case UINavigationControllerOperationPush:
{
CGRect frame = CGRectMake(0,0,[UIScreenmainScreen].bounds.size.width,[UIScreenmainScreen].bounds.size.height);
toViewController.view.frame=
frame;
[containerViewaddSubview:toViewController.view];
toViewController.view.alpha=0.0;
[UIViewanimateWithDuration:duration
animations:^{
toViewController.view.alpha=1.0;
} completion:^(BOOLfinished) {
[fromViewController.viewremoveFromSuperview];
[transitionContext completeTransition:YES];
}];
}
break;
case UINavigationControllerOperationPop:
{
[containerView insertSubview:toViewController.viewbelowSubview:fromViewController.view];
CGRect frame = [transitionContextinitialFrameForViewController:fromViewController];
fromViewController.view.frame= frame;
[UIViewanimateWithDuration:duration
animations:^{
fromViewController.view.frame=CGRectMake(0, frame.origin.y+ [UIScreenmainScreen].bounds.size.height, frame.size.width, frame.size.height);
} completion:^(BOOLfinished) {
[fromViewController.viewremoveFromSuperview];
[transitionContext completeTransition:YES];
}];
}
break;
default:
break;
}
toViewController.view.alpha=0.0;
[UIViewanimateWithDuration:duration
animations:^{
toViewController.view.alpha=1.0;
} completion:^(BOOLfinished) {
[fromViewController.viewremoveFromSuperview];
[transitionContext completeTransition:YES];
}];
}
break;
case UINavigationControllerOperationPop:
{
[containerView insertSubview:toViewController.viewbelowSubview:fromViewController.view];
CGRect frame = [transitionContextinitialFrameForViewController:fromViewController];
fromViewController.view.frame= frame;
[UIViewanimateWithDuration:duration
animations:^{
fromViewController.view.frame=CGRectMake(0, frame.origin.y+ [UIScreenmainScreen].bounds.size.height, frame.size.width, frame.size.height);
} completion:^(BOOLfinished) {
[fromViewController.viewremoveFromSuperview];
[transitionContext completeTransition:YES];
}];
}
break;
default:
break;
}
}
(2)将这个实现UIViewControllerAnimatedTransitioning的类的实例赋值给UINavigationController的delegate的回调方法。代码如下:
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController*)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController*)fromVC
toViewController:(UIViewController*)toVC
{
if (!_manager) {
_manager = [CyPushPopManagernew];
}
_manager.operation= operation;
return _manager;
{
if (!_manager) {
_manager = [CyPushPopManagernew];
}
_manager.operation= operation;
return _manager;
}
(3)效果如下
3.push/pop转场动画的tips
在iOS7中,UINavigationController中有一个interactivePopGestureRecognizer的属性,支持APP的滑动返回这个功能。但是,在自定义UINavigationController过程,或者是自定义UIViewController的leftBarButtonItem就会导致这个gesture失效。解决方案:self.interactivePopGestureRecognizer.delegate=
(id)self;重新复制这个gesture的delegate给self就OK了。愿意还不懂。