iOS 7学习:定制View Controllers之间的切换动画

原文地址:http://blog.csdn.net/jymn_chen/article/details/18944323

iOS 7新出了一个非常好玩的特性,就是View Controllers之间的切换动画,开发者也可以在程序中自行定制切换动画,例如翻转视图、缩放视图、旋转视图等等。另一方面,对于iPhone的使用来说,通过pan手势pop回到之前的视图,使用起来无疑是非常方便的。最开始使用这个特性时感觉非常惊喜。

本文就来说说如何定制我们自己的动画切换方案。

首先定义一个实现UINavigationControllerDelegate协议的类NavigationPerfomer。

故事板非常简单,就是一个UINavigationController和两个View Controller。但是要在UINavigationController中加入一个Object(这个貌似是Xcode 5的新特性吧,以前比较少用故事板,所以不是很确定),该对象的类为NavigaitonPerfomer类:



随后要为NavigationPerformer添加一个outlet:当前的UINavigatinController。并将UINavigationController的委托设为NavigationPerformer类。


故事板中其他的View Controller一切照旧,这里做得非常好,在提供了新的动画接口之后,在UINavigationController类中实现这些接口并不会影响到其他的View Controller的内容。

接着定义两个动画类:PushAnimation和PopAnimation,分别对应Push和Pop动画。

代码如下:

PushAnimtaion类:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #import <Foundation/Foundation.h>  
  2.   
  3. @interface PushAnimation : NSObject <UIViewControllerAnimatedTransitioning>  
  4.   
  5. @end  
[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #pragma mark - UIViewControllerAnimatedTransitioning Delegate  
  2.   
  3. /* 动画持续的时间 */  
  4. - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {  
  5.     return 1;  
  6. }  
  7.   
  8. /* 动画的内容 */  
  9. - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {  
  10.     UIViewController *desController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];  
  11.     UIViewController *srcController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];  
  12.     [[transitionContext containerView] addSubview:desController.view];  
  13.     desController.view.alpha = 0.0;  
  14.       
  15.     [UIView animateWithDuration:[self transitionDuration:transitionContext]  
  16.                      animations:^{  
  17.                          srcController.view.transform = CGAffineTransformMakeTranslation(-srcController.view.bounds.size.width0.0);  
  18.                          desController.view.alpha = 1.0;  
  19.                      }  
  20.                      completion:^(BOOL finished) {  
  21.                          srcController.view.transform = CGAffineTransformIdentity;  
  22.                          [transitionContext completeTransition:![transitionContext transitionWasCancelled]];  
  23.                      }];  
  24. }  

动画类必须实现 UIViewControllerAnimatedTransitioning协议。

协议中的两个必选方法:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // This is used for percent driven interactive transitions, as well as for container controllers that have companion animations that might need to  
  2. // synchronize with the main animation.   
  3. - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext;  
  4. // This method can only  be a nop if the transition is interactive and not a percentDriven interactive transition.  
  5. - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;  

第一个方法指定动画持续的时间,以秒为单位。

第二个方法定制动画的内容。通过transitionContext这个上下文环境可以获取源视图控制器,目标视图控制器:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. UIViewController *desController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];  
  2. UIViewController *srcController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];  

在进行动画前,先要将desController的视图添加到当前容器视图中。



[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. + (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(4_0); // delay = 0.0, options = 0  

该方法才是真正定义动画内容的关键,duration参数指定动画持续的时间,animations代码块给出要执行的变换,当动画完成后调用completion代码块通知transition context,在这里可以进行如设置视图的alpha、还原仿射变换等动作。


这里Push Animation的动画方案是将srcViewController自左向右划出窗口。


另外一个是PopAnimation类:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @interface PopAnimation : NSObject <UIViewControllerAnimatedTransitioning>  
  2.   
  3. @end  

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #pragma mark - UIViewControllerAnimatedTransitioning Delegate  
  2.   
  3. - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {  
  4.     return 1;  
  5. }  
  6.   
  7. - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {  
  8.     UIViewController *desController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];  
  9.     UIViewController *srcController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];  
  10.     [[transitionContext containerView] addSubview:desController.view];  
  11.     desController.view.alpha = 0.0;  
  12.       
  13.     [UIView animateWithDuration:[self transitionDuration:transitionContext]  
  14.                      animations:^{  
  15.                          srcController.view.transform = CGAffineTransformMakeScale(-1.01.0);  
  16.                          desController.view.alpha = 1.0;  
  17.                      }  
  18.                      completion:^(BOOL finished) {  
  19.                          srcController.view.transform = CGAffineTransformIdentity;  
  20.                          [transitionContext completeTransition:![transitionContext transitionWasCancelled]];  
  21.                      }];  
  22. }  
  23.   
  24. @end  

这里pop的动画方案是翻转srcController的视图。


接着是关键的NavigationPerformer类:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #import <Foundation/Foundation.h>  
  2.   
  3. @class PushAnimation;  
  4. @class PopAnimation;  
  5.   
  6. @interface NavigationPerfomer : NSObject <UINavigationControllerDelegate>  
  7.   
  8. @property (weak, nonatomic) IBOutlet UINavigationController *navigationController;  
  9.   
  10. @property (strongnonatomicPushAnimation *pushAnimation;  
  11.   
  12. @property (strongnonatomicPopAnimation *popAnimation;  
  13.   
  14. @property (strongnonatomicUIPercentDrivenInteractiveTransition *interactionController;  
  15.   
  16. @end  

注意,interactionController类非常有用,只有通过该类才能使视图响应用户的手势输入。

首先看看UINavigationControllerDelegate的方法:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #pragma mark - UINavigationController Delegate  
  2.   
  3. - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController  
  4.                                   animationControllerForOperation:(UINavigationControllerOperation)operation  
  5.                                                fromViewController:(UIViewController *)fromVC  
  6.                                                  toViewController:(UIViewController *)toVC  
  7. {  
  8.     if (operation == UINavigationControllerOperationPush) {  
  9.         return self.pushAnimation;  
  10.     }  
  11.     else if (operation == UINavigationControllerOperationPop) {  
  12.         return self.popAnimation;  
  13.     }  
  14.       
  15.     return nil;  
  16. }  
  17.   
  18. - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {  
  19.     return self.interactionController;  
  20. }  

第一个方法指定不同的动作下的动画方案,在返回对象时,这些对象必须实现UIViewControllerAnimatedTransitioning协议。

第二个方法指定提供交互性的控制器对象,如果返回nil,那么视图将无法响应用户的手势,如滑动等。


下面为导航控制器的视图添加一个Pan手势:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. - (void)awakeFromNib {  
  2.     // 在导航控制器的视图上添加pan手势  
  3.     UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];  
  4.     [self.navigationController.view addGestureRecognizer:panGesture];  
  5.       
  6.     // 初始化动画方案  
  7.     self.pushAnimation = [[PushAnimation alloc] init];  
  8.     self.popAnimation  = [[PopAnimation alloc] init];  
  9. }  
  10.   
  11. - (void)pan:(UIPanGestureRecognizer *)pan {  
  12.     UIView *view = self.navigationController.view;  
  13.     if (pan.state == UIGestureRecognizerStateBegan) { // 只有从左向右滑动的手势才作出响应:pop  
  14.         CGPoint location = [pan locationInView:view];  
  15.         if (location.x < CGRectGetMidX(view.bounds) && self.navigationController.viewControllers.count > 1) {  
  16.             self.interactionController = [[UIPercentDrivenInteractiveTransition alloc] init];  
  17.             [self.navigationController popViewControllerAnimated:YES];  
  18.         }  
  19.     }  
  20.     else if (pan.state == UIGestureRecognizerStateChanged) { // 以当前的位移作为进度执行动画  
  21.         CGPoint translation = [pan translationInView:view];  
  22.         CGFloat progress = fabs(translation.x / CGRectGetWidth(view.bounds));  
  23.         [self.interactionController updateInteractiveTransition:progress];  
  24.     }  
  25.     else if (pan.state == UIGestureRecognizerStateEnded) {  
  26.         if ([pan velocityInView:view].x > 0) {  
  27.             [self.interactionController finishInteractiveTransition];  
  28.         }  
  29.         else { // 如果手势的终点相对于起点在x正向上的位移小于0,即为取消手势  
  30.             [self.interactionController cancelInteractiveTransition];  
  31.         }  
  32.         self.interactionController = nil// 最后必须将interactionController清空,确保不会影响到后面的动画执行  
  33.     }  
  34. }  

在pan:方法的执行过程中,程序会根据手势位移的百分比作为进度播放动画。

在切换动画完毕后,一定要将interactionController设为nil,因为某些动画是不需要手势交互的,这样会带来奇怪的行为。


最后是一些运行结果:

Push时的效果:



使用Pan手势Pop时的效果:



通过这个新的Transition接口我们可以实现一些比较好玩的动画,Demo已经上传,有兴趣的可以下载看看。

这部分还是挺好玩的,Just enjoy.


参考资料:

iOS 7 新特性:视图控制器切换API

objc.io #5 View Controller Transitions

objcio / issue5-view-controller-transitions


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值