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其实是转场动画的实施者,动画的具体形式,时间都是通过这个协议设置的,自定义的主要内容。
- (
nullable
id
<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 = [transitionContext
viewControllerForKey
:
UITransitionContextToViewControllerKey
];
NSTimeInterval duration = [ self transitionDuration :transitionContext];
UIView * containerView = [transitionContext containerView ];
switch ( self . operation ) {
case UINavigationControllerOperationPush :
{
CGRect frame = CGRectMake ( 0 , 0 ,[ UIScreen mainScreen ]. bounds . size . width ,[ UIScreen mainScreen ]. bounds . size . height );
NSTimeInterval duration = [ self transitionDuration :transitionContext];
UIView * containerView = [transitionContext containerView ];
switch ( self . operation ) {
case UINavigationControllerOperationPush :
{
CGRect frame = CGRectMake ( 0 , 0 ,[ UIScreen mainScreen ]. bounds . size . width ,[ UIScreen mainScreen ]. bounds . size . height );
toViewController.view.frame= frame;
[containerView
addSubview
:toViewController.
view
];
toViewController. view . alpha = 0.0 ;
[ UIView animateWithDuration :duration
animations :^{
toViewController. view . alpha = 1.0 ;
} completion :^( BOOL finished) {
[fromViewController. view removeFromSuperview ];
[transitionContext completeTransition : YES ];
}];
}
break ;
case UINavigationControllerOperationPop :
{
[containerView insertSubview :toViewController. view belowSubview :fromViewController. view ];
CGRect frame = [transitionContext initialFrameForViewController :fromViewController];
fromViewController. view . frame = frame;
[ UIView animateWithDuration :duration
animations :^{
fromViewController. view . frame = CGRectMake ( 0 , frame. origin . y + [ UIScreen mainScreen ]. bounds . size . height , frame. size . width , frame. size . height );
} completion :^( BOOL finished) {
[fromViewController. view removeFromSuperview ];
[transitionContext completeTransition : YES ];
}];
}
break ;
default :
break ;
}
toViewController. view . alpha = 0.0 ;
[ UIView animateWithDuration :duration
animations :^{
toViewController. view . alpha = 1.0 ;
} completion :^( BOOL finished) {
[fromViewController. view removeFromSuperview ];
[transitionContext completeTransition : YES ];
}];
}
break ;
case UINavigationControllerOperationPop :
{
[containerView insertSubview :toViewController. view belowSubview :fromViewController. view ];
CGRect frame = [transitionContext initialFrameForViewController :fromViewController];
fromViewController. view . frame = frame;
[ UIView animateWithDuration :duration
animations :^{
fromViewController. view . frame = CGRectMake ( 0 , frame. origin . y + [ UIScreen mainScreen ]. bounds . size . height , frame. size . width , frame. size . height );
} completion :^( BOOL finished) {
[fromViewController. view removeFromSuperview ];
[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 = [ CyPushPopManager new ];
}
_manager . operation = operation;
return _manager ;
{
if (! _manager ) {
_manager = [ CyPushPopManager new ];
}
_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了。愿意还不懂。