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
)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
{
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
);
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
;
}
}
(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
;
}
(3)效果如下
3.push/pop转场动画的tips
在iOS7中,UINavigationController中有一个interactivePopGestureRecognizer的属性,支持APP的滑动返回这个功能。但是,在自定义UINavigationController过程,或者是自定义UIViewController的leftBarButtonItem就会导致这个gesture失效。解决方案:
self
.
interactivePopGestureRecognizer
.
delegate
= (
id
)
self
;重新复制这个gesture的delegate给self就OK了。愿意还不懂。