在iOS程序里出现得最多的转场动画,就是UINavigationController的Push和Pop了,看多了就觉得有些无聊了,还好苹果提供了自定义转场动画的API,往下看。
首页,要明白既然转场动画是通过导航控制器来完成(UIViewController模态除外),那么就往UINavigationController看,既然是转场,那么在两个控制器切换的中间,就是转场动画发生的地方,一提到“将要出现”,“已经出现”,“将要消失”,“已经消失”,条件反射就想到了UINavigationController的代理方法:
@protocol UINavigationControllerDelegate <NSObject>
@optional
// 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;
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
- (NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController NS_AVAILABLE_IOS(7_0);
- (UIInterfaceOrientation)navigationControllerPreferredInterfaceOrientationForPresentation:(UINavigationController *)navigationController NS_AVAILABLE_IOS(7_0);
- (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController NS_AVAILABLE_IOS(7_0);
- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
@end
注意最后两个方法,就是它了,注意可用的iOS版本: NS_AVAILABLE_IOS ( 7 _0)
- (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController NS_AVAILABLE_IOS(7_0);
这个方法是讲的手势百分比交互,今天不谈,说最后一个方法,它的返回值是
id <UIViewControllerAnimatedTransitioning>,一个遵循UIViewControllerAnimatedTransitioning的对象,点开它:
@protocol UIViewControllerAnimatedTransitioning <NSObject>
// This is used for percent driven interactive transitions, as well as for container controllers that have companion animations that might need to
// synchronize with the main animation.
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext;
// This method can only be a nop if the transition is interactive and not a percentDriven interactive transition.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
@optional
两个方法,第一个是返回动画持续时间,第二个是动画具体内容。
说了这么多理论,总结一下自定义转场动画大致的设置思路:
1、自定义一个继承自NSObject并且遵从UIViewControllerAnimatedTransitioning协议的类,里面实现协议方法,设置好动画时长和具体内容;
2、在转场动画发生的viewcontroller里,遵从UINavigationControllerDelegate协议,实现协议方法
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
在该方法里返回自定义的动画类的一个对象即可。
示例:
新建一个有两个viewcontroller的工程:FirstViewController、SecondViewcontroller,将FirstViewcontroller加到一个导航控制器里,它的view上有一个“push”按钮,点它可以将SecondViewController push进来,SecondViewController上有一个“pop”按钮,点它可以pop到FirstViewController,描述得已经很清楚了吧。
创建两个继承自NSObject的类,一个叫PushAnimation,一个叫PopAnimation,它们都遵循UIViewControllerAnimatedTransitioning协议:
@interface PushAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@end
@interface PopAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@end
在PushAnimation.h
//返回动画持续时间
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 2.0f;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
//获取起点controller
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//获取终点controller
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//获取转场容器视图
UIView *containerView = [transitionContext containerView];
//设置终点视图的frame
CGRect frame = [transitionContext initialFrameForViewController:fromVC];
CGRect offScreenFrame = frame;
//先将其设置到屏幕外边,通过动画进入
offScreenFrame.origin.x = offScreenFrame.size.width;
toVC.view.frame = offScreenFrame;
//添加视图
[containerView addSubview:toVC.view];
//执行动画
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
//设置缩放和透明度
fromVC.view.transform = CGAffineTransformMakeScale(0.8, 0.8);
fromVC.view.alpha = 0.5;
//设置位置
toVC.view.frame = frame;
} completion:^(BOOL finished) {
fromVC.view.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
在PopAnimation里实现下面方法(push的逆动画,其实可以只创建一个类,通过“顺”or“逆”来判断执行的动画):
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return 2.0f;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = [transitionContext containerView];
toVC.view.transform = CGAffineTransformMakeScale(0.8, 0.8);
toVC.view.alpha = 0.5;
CGRect frame = [transitionContext initialFrameForViewController:fromVC];
CGRect offScreenFrame = frame;
offScreenFrame.origin.x = offScreenFrame.size.width;
[containerView insertSubview:toVC.view belowSubview:fromVC.view];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
toVC.view.transform = CGAffineTransformIdentity;
toVC.view.alpha = 1.0f;
fromVC.view.frame = offScreenFrame;
} completion:^(BOOL finished) {
fromVC.view.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
在FirstViewController.m里添加下面代码:
#import "PushAnimation.h"
@interface FirstViewController ()<UINavigationControllerDelegate>
{
PushAnimation *_pushAni;
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
_pushAni = [PushAnimation new];
}
- (void)viewDidAppear:(BOOL)animated
{
self.navigationController.delegate = self;
[super viewDidAppear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
if (self.navigationController.delegate == self) {
self.navigationController.delegate = nil;
}
[super viewDidDisappear:animated];
}
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
if (operation == UINavigationControllerOperationPush) {
return _pushAni;
}
return nil;
}
同样,在SecondViewController里设置pop的动画代码,即可,更好的方法,可以写一个继承自UINavigationController的子类,统一实现协议方法。