经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
iOS:探究视图控制器的转场动画
来源:cnblogs  作者:菩提树下苦思  时间:2019/11/11 8:47:49  对本文有异议

一、介绍

在iOS开发中,转场动画的使用无处不见,不只是我们自己更多的使用UIViewblock动画实现一个转场动画,其实,在我们实现VC控制器跳转的时候都是转场动画的实现,例如标签栏控制器的切换、模态动画present和dismiss、导航控制器的push和pop。实现它们的转场动画,只需要实现它们的动画协议即可,说起来有点太笼统,不如看下面的图吧:

 

二、分析

对于上面的三种类型的控制器,系统都会为它们设置一个代理,通过这个代理方法去监测它们切换VC的过程,这个过程仅仅是出现和消失的过程,至于这个过程是什么过渡效果,这个代理是不管的。要想这个过程是有动画的,那么在这些过程中,也就是代理函数中,需要另外再返回一个实现动画的对象,这个对象必须遵循实现动画的协议,在这个协议中开发者可以重写自定义转场动画。下面会慢慢演示这三种类型控制器的自定义转场动画。

重写不可交互转场动画的核心协议内容:

  1. //重写动画协议
  2. @protocol UIViewControllerAnimatedTransitioning <NSObject>
  3.  
  4. //动画执行时间
  5. - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
  6. //自定义动画效果
  7. - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
  8. @end

重写可交互转场动画的核心协议内容:

  1. //重写动画协议
  2. @protocol UIViewControllerInteractiveTransitioning <NSObject>
  3.  
  4. //自定义动画效果
  5. - (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
  6. @end

系统提供的一个百分比可交互转场动画核心类内容:

  1. //系统提供的百分比动画类,已经遵循了可交互协议
  2. @interface UIPercentDrivenInteractiveTransition : NSObject <UIViewControllerInteractiveTransitioning>
  3. - (void)pauseInteractiveTransition;
  4. - (void)updateInteractiveTransition:(CGFloat)percentComplete;
  5. - (void)cancelInteractiveTransition;
  6. - (void)finishInteractiveTransition;
  7. @end

 

三、转场动画View之间的切换

 

四、实现一个自定义的模态动画

1、概述

正如我们所知,系统为我们提供的模态动画默认是从底部present出,然后dismiss回到底部。 虽然说这个基本能够满足使用,但是如果我们还想使用其他形式的模态动画例如从顶部present出dismiss回到顶部,这个时候就需要对系统默认的转场动画进行自定义了。

2、详解

(1)要自定义模态转场动画,首先需要给被模态的控制器设置一个实现了UIViewControllerAnimatedTransitioning协议的代理,这些协议方法可以监测动画执行的过程,代理和协议如下:

  1. //代理
  2. @protocol UIViewControllerTransitioningDelegate;
  3. @interface UIViewController(UIViewControllerTransitioning)
  4. @property (nullable, nonatomic, weak) id <UIViewControllerTransitioningDelegate> transitioningDelegate API_AVAILABLE(ios(7.0));
  5. @end
  1. //协议
    @protocol
    UIViewControllerTransitioningDelegate <NSObject>
  2. @optional
  3. //present时调用,返回一个实现了不可交互转场动画协议的代理
  4. - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
  5. //dismiss时调用,返回一个实现了不可交互转场动画协议的代理
  6. - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
  7. //presnt过程中交互时调用,返回一个实现了可交互的转场动画协议的代理
  8. - (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator;
  9. //dismiss过程中交互时调用,返回一个实现了可交互的转场动画协议的代理
  10. - (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;
  11. //返回新的模态弹框控制器(这个是对模态风格进行自定义时调用,后面会说到)
  12. - (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(nullable UIViewController *)presenting sourceViewController:(UIViewController *)source API_AVAILABLE(ios(8.0));
  13. @end

(2)然后在上面的协议方法中返回一个实现了UIViewControllerAnimatedTransitioning协议的代理,在这个代理的协议方法中可以真正重写转场动画了,协议如下:

  1. @protocol UIViewControllerAnimatedTransitioning <NSObject>
  2. //动画执行时间
  3. - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
  4. //自定义转场动画
  5. - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
  6. @optional

(3)自定义转场动画实现如下【注意:Singleton单例类和UIView+Extesion分类需要自己去拷贝引入】

  • 设置UIViewControllerAnimatedTransitioning代理对象TransitionDelegate,监测动画执行过程,将其设置为单例
    1. #import <UIKit/UIKit.h>
    2. #import "Singleton.h"
    3. @interface TransitionDelegate : NSObject<UIViewControllerTransitioningDelegate>
    4. SingletonH(TransitionDelegate);
    5. @end
    1. #import "TransitionDelegate.h"
    2. #import "CustomAnimationTransition.h"
    3.  
    4. @implementation TransitionDelegate
    5. SingletonM(TransitionDelegate);
    6. #pragma mark - <UIViewControllerTransitioningDelegate>
    7.  
    8. //展示的动画
    9. - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
    10. {
    11. CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init];
    12. animation.presented = YES;
    13. return animation;
    14. }
    15. //关闭的动画
    16. - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
    17. {
    18. CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init];
    19. animation.presented = NO;
    20. return animation;
    21. }
    22. @end
  • 设置UIViewControllerAnimatedTransitioning代理对象CustomTransitionAnimationTransition,重写动画效果
    1. #import <UIKit/UIKit.h>
    2.  
    3. @interface CustomAnimationTransition : NSObject<UIViewControllerAnimatedTransitioning>
    4. //判断是present还是dismiss, YES:present NO:dismisss
    5. @property (assign,nonatomic)BOOL presented;
    6. @end
    1. //设置过渡动画(modal和dismiss的动画都需要在这里处理)
    2. - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
    3. {
    4. // UITransitionContextToViewKey,
    5. // UITransitionContextFromViewKey.
    6. //出来的动画
    7. if (self.presented) {
    8. //获取并添加转场视图
    9. UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    10. [transitionContext.containerView addSubview:toView];
    11. //设置动画从上往下出来
    12. toView.y = -toView.height;
    13. [UIView animateWithDuration:duration animations:^{
    14. toView.y = 0;
    15. } completion:^(BOOL finished) {
    16. //移除视图
    17. BOOL cancle = [transitionContext transitionWasCancelled];
    18. if (cancle) {
    19. [toView removeFromSuperview];
    20. }
    21. //动画完成后,视图上的事件才能处理
    22. [transitionContext completeTransition:!cancle];
    23. }];
    24. }
    25. //销毁的动画
    26. else
    27. {
    28. //获取转场视图
    29. UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    30. [UIView animateWithDuration:duration animations:^{
    31. fromView.y = -fromView.height;
    32. } completion:^(BOOL finished) {
    33. //移除视图
    34. BOOL cancle = [transitionContext transitionWasCancelled];
    35. if (!cancle) {
    36. [fromView removeFromSuperview];
    37. }
    38. //动画完成后,视图上的事件才能处理
    39. [transitionContext completeTransition:!cancle];
    40. }];
    41. }
    42. }
  • 开始执行,结果如gif图 
    1. //present
    2. UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]];
    3. nav.transitioningDelegate = [TransitionDelegate sharedTransitionDelegate];//自定义转场动画
    4. nav.modalPresentationStyle = UIModalPresentationFullScreen;
      [self presentViewController:nav animated:YES completion:nil];

 (4)我们已经实现了一个简单的自定义模态不可交互的转场动画,其实,在模态控制器的时候,我们还可以自定义可交互的转场动画以及设置自定义的模态风格。可交互的转场动画一会儿再讨论,先来讨论一下模态风格,系统在iOS13之前默认都是满屏模式的UIModalPresentationFullScreen,但是iOS13之后,默认是UIModalPresentationPageSheet。系统提供的模态风格如下:

  1. //模态风格枚举
  2. typedef NS_ENUM(NSInteger, UIModalPresentationStyle) {
  3. UIModalPresentationFullScreen = 0,
  4. UIModalPresentationPageSheet ,
  5. UIModalPresentationFormSheet ,
  6. UIModalPresentationCurrentContext ,
  7. UIModalPresentationCustom , //自定义
  8. UIModalPresentationOverFullScreen ,
  9. UIModalPresentationOverCurrentContext ),
  10. UIModalPresentationPopover ,
  11. UIModalPresentationBlurOverFullScreen ,
  12. UIModalPresentationNone,
  13. UIModalPresentationAutomatic ,
  14. };

 (5)从上面的枚举可以看到,系统是支持我们实现自己的风格的,也就是自定义。在实现自定义之前,一定得知道UIPresentationController这个类,这个是弹出框控件,模态的控制器都是由它进行管理,主要代码如下:

  1. //重写此方法可以在弹框即将显示时执行所需要的操作
  2. - (void)presentationTransitionWillBegin;
  3. //重写此方法可以在弹框显示完毕时执行所需要的操作
  4. - (void)presentationTransitionDidEnd:(BOOL)completed;
  5. //重写此方法可以在弹框即将消失时执行所需要的操作
  6. - (void)dismissalTransitionWillBegin;
  7. //重写此方法可以在弹框消失之后执行所需要的操作
  8. - (void)dismissalTransitionDidEnd:(BOOL)completed;
  9. //重写决定了弹出框的frame
  10. - (CGRect)frameOfPresentedViewInContainerView;
  11. //重写对containerView进行布局
  12. - (void)containerViewWillLayoutSubviews;
  13. - (void)containerViewDidLayoutSubviews;
  14. //初始化方法
  15. - (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController;

(6)额外再提一个知识点,因为一会儿在自定义模态风格时会涉及到。在本文开篇结构图中介绍了创建的转场动画都是在转场动画上下文UIViewControllerContextTransitioning协议中完成的,那么这个转场动画的执行是谁管理呢?看结构图如下,没错,是由UIViewControllerTransitionCoordinator这个代理协调器在协调器上下文中完成的,系统给UIViewController提供了一个分类,这个分类持有这个代理协调器,通过这个代理协调器可以拿到执行转场动画的方法。最终,我们可以自己添加一些操作与转场动画同步执行。

 

UIViewControllerContextTransitioning协议核心内容

  1. @protocol UIViewControllerTransitionCoordinatorContext <NSObject>
  2. // 执行的属性
  3. @property(nonatomic, readonly, getter=isAnimated) BOOL animated;
  4. @property(nonatomic, readonly) UIModalPresentationStyle presentationStyle;
  5. @property(nonatomic, readonly) NSTimeInterval transitionDuration;
  6. @property(nonatomic, readonly) UIView *containerView;
  7. @property(nonatomic, readonly) CGAffineTransform targetTransform
  8. // 参与控制器
  9. // UITransitionContextToViewControllerKey、UITransitionContextFromViewControllerKey
  10. - (nullable __kindof UIViewController *)viewControllerForKey:(UITransitionContextViewControllerKey)key;
  11. // 参与的视图
  12. // UITransitionContextToViewKey、UITransitionContextFromViewKey
  13. - (nullable __kindof UIView *)viewForKey:(UITransitionContextViewKey)key API_AVAILABLE(ios(8.0));
  14. @end

UIViewControllerTransitionCoordinator协议核心内容

  1. // 与动画控制器中的转场动画同步,执行其他动画
  2. - (BOOL)animateAlongsideTransition:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))animation
  3. completion:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))completion;
  4. // 与动画控制器中的转场动画同步,在指定的视图内执行动画
  5. - (BOOL)animateAlongsideTransitionInView:(nullable UIView *)view
  6. animation:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))animation
  7. completion:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))completion;

UIViewController(UIViewControllerTransitionCoordinator) 分类核心内容

  1. //持有转场动画执行协调器
  2. @interface UIViewController(UIViewControllerTransitionCoordinator)
  3. @property(nonatomic, readonly, nullable) id <UIViewControllerTransitionCoordinator> transitionCoordinator;
  4. @end

(7)自定义模态风格实现如下【注意:Singleton单例类和UIView+Extesion分类需要自己去拷贝引入】

  • 设置UIViewControllerAnimatedTransitioning代理对象TransitionDelegate,监测动画执行过程并返回模态风格,将其设置为单例
    1. #import <UIKit/UIKit.h>
    2. #import "Singleton.h"
    3.  
    4. @interface TransitionDelegate : NSObject<UIViewControllerTransitioningDelegate>
    5. SingletonH(TransitionDelegate);
    6. @end
    1. #import "TransitionDelegate.h"
    2. #import "CustomPresentationController.h"
    3. #import "CustomAnimationTransition.h"
    4.  
    5. @implementation TransitionDelegate
    6. SingletonM(TransitionDelegate);
    7. #pragma mark - <UIViewControllerTransitioningDelegate>
    8. //返回模态风格
    9. -(UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
    10. {
    11. return [[CustomPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
    12. }
    13. //展示的动画
    14. - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
    15. {
    16. CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init];
    17. animation.presented = YES;
    18. return animation;
    19. }
    20. //关闭的动画
    21. - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
    22. {
    23. CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init];
    24. animation.presented = NO;
    25. return animation;
    26. }
    27. @end
  • 设置UIViewControllerAnimatedTransitioning代理对象CustomTransitionAnimationTransition,重写动画效果
    1. #import <UIKit/UIKit.h>
    2.  
    3. @interface CustomAnimationTransition : NSObject<UIViewControllerAnimatedTransitioning>
    4. //判断是present还是dismiss, YES:present NO:dismisss
    5. @property (assign,nonatomic)BOOL presented;
    6. @end
    1. #import "CustomAnimationTransition.h"
    2. #import "UIView+Extension.h"
    3.  
    4. const CGFloat duration = 0.5f;
    5. @implementation CustomAnimationTransition
    6. #pragma mark -<UIViewControllerAnimatedTransitioning>
    7. //动画时间
    8. - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    9. {
    10. return duration;
    11. }
    12. //设置过渡动画(modal和dismiss的动画都需要在这里处理)
    13. - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
    14. {
    15. // UITransitionContextToViewKey,
    16. // UITransitionContextFromViewKey.

    17.    //发现此处并没有添加toView到containerView中以及从containerView中移除toView,与上面的有区别。
      //我把添加和移除toView的操作放到了下面的自定义的模态风格类中完成的

      //
      出来的动画
    18. if (self.presented) {
    19. UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    20. //设置动画从上往下出来
    21. toView.y = -toView.height;
    22. [UIView animateWithDuration:duration animations:^{
    23. toView.y = 0;
    24. } completion:^(BOOL finished) {
    25. //动画完成后,视图上的事件才能处理
    26. [transitionContext completeTransition:YES];
    27. }];
    28. }
    29. //销毁的动画
    30. else
    31. {
    32. [UIView animateWithDuration:duration animations:^{
    33. UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    34. fromView.y = -fromView.height;
    35. } completion:^(BOOL finished) {
    36. //动画完成后,视图上的事件才能处理
    37. [transitionContext completeTransition:YES];
    38. }];
    39. }
    40. }
  • 设置自定义的模态风格类CustomPresenttationController
    1. #import "CustomPresentationController.h"
    2.  
    3. @implementation CustomPresentationController
    4. //可以改变被模态的控制器视图的尺寸
    5. - (CGRect)frameOfPresentedViewInContainerView
    6. {
         //CGRectInset: 在containerView的frame基础上,将width减小100,将height减小200
    7. //containerView是容纳presentedView的一个容器
    8. return CGRectInset(self.containerView.bounds, 50, 100);
    9. }
    10. //将上面重置的frame完成布局
    11. - (void)containerViewDidLayoutSubviews {
    12. self.presentedView.frame = self.frameOfPresentedViewInContainerView;
    13. [super containerViewDidLayoutSubviews];
    14. }
    15. //过渡即将展示时的处理
    16. //这个过程可以改变视图属性、或者添加视图等
    17. - (void)presentationTransitionWillBegin
    18. {
    19. self.presentedView.frame = self.containerView.frame;
    20. [self.containerView addSubview:self.presentedView];
    21. }
    22. //过渡展示完成
    23. //做清理工作
    24. - (void)presentationTransitionDidEnd:(BOOL)completed
    25. {
    26. if (!completed) {
    27. [self.presentedView removeFromSuperview];
    28. }
    29. }
    30. //过渡即将消失时的处理
    31. //这个过程可以改变视图属性等
    32. - (void)dismissalTransitionWillBegin
    33. {
    34. //例如改变透明度,与转场控制器中的转场动画同步执行
    35. [self.presentingViewController.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
    36. self.presentedView.alpha = 0.f;
    37. } completion:nil];
    38. }
    39. //过渡消失完成
    40. //做清理工作
    41. - (void)dismissalTransitionDidEnd:(BOOL)completed
    42. {
    43. if (completed) {
    44. [self.presentedView removeFromSuperview];
    45. }
    46. }
    47. @end
  • 开始执行,结果如gif图
    1. //present
    2. UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]];
    3. nav.transitioningDelegate = [TransitionDelegate sharedTransitionDelegate];//自定义转场动画
    4. nav.modalPresentationStyle = UIModalPresentationCustom; //自定义模态风格
    5. [self presentViewController:nav animated:YES completion:nil];

 (8)自定义模态转场动画和自定义模态风格我们都实现完了,但是上面的动画过程中都是不可交互的,那么要想实现可交互的动画该怎么做呢?如上面所说的,在dismiss时返回一个实现了UIViewControllerInteractiveTransitioning协议的代理或者直接是原生类UIPercentDrivenInteractiveTransition对象。其中,UIPercentDrivenInteractiveTransition是系统封装好了百分比驱动,用起来很简单,那么真正的实现原理还是我们去实现一下。下面咱们来实现导航模式的交互效果,如下:

  • TransitioningDelegate
    1. #import <UIKit/UIKit.h>
    2. #import "Singleton.h"

      @interface TransitioningDelegate : NSObject<UIViewControllerTransitioningDelegate>
    3. SingletonH(TransitioningDelegate);
    4. @end
    1. #import "TransitioningDelegate.h"
    2. #import "CustomAnimationTransition.h"
    3. #import "CustomInteractiveTransition.h"
    4.  
    5. @implementation TransitioningDelegate
    6. SingletonM(TransitioningDelegate);
    7. #pragma mark - UIViewControllerTransitioningDelegate
    8.  
    9. //present
    10. - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
    11. //这里还是采用自定义的转场动画方式进行present,使其present时从屏幕右侧滑入
      CustomAnimationTransition
      *animation = [[CustomAnimationTransition alloc]init];
    12. animation.presented = YES;
    13. return animation;
    14. }
    15. //dismiss,必须重写
    16. - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
    17. //这里采用自定义的转场动画覆盖系统的dismiss效果,在dismiss时,由于自定义了交互动画,所以系统自己的dismiss动画不会执行
      CustomAnimationTransition
      *animation = [[CustomAnimationTransition alloc] init];
    18. animation.presented = NO;
    19. return animation;
    20. }
    21. //将要dismiss时的交互行为,必须重写
    22. - (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator {
    23. CustomInteractiveTransition *animation = [[CustomInteractiveTransition alloc] init];
    24. return animation;
    25. }
    26. @end
  • CustomAnimationTransition
    1. #import <UIKit/UIKit.h>
    2.  
    3. @interface CustomAnimationTransition : NSObject<UIViewControllerAnimatedTransitioning>
    4. //判断是present还是dismiss, YES:present NO:dismisss
    5. @property (assign,nonatomic)BOOL presented;
    6. @end
    1. #import "CustomAnimationTransition.h"
    2. #import "UIView+Extension.h"
    3.  
    4. const CGFloat duration = 0.5f;
    5. @implementation CustomAnimationTransition
    6. #pragma mark -<UIViewControllerAnimatedTransitioning>
    7. //动画时间
    8. - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    9. {
    10. return duration;
    11. }
    12. //设置过渡动画(modal和dismiss的动画都需要在这里处理)
    13. - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
    14. {
    15. // UITransitionContextToViewKey,
    16. // UITransitionContextFromViewKey.
    17. //出来的动画
    18. if (self.presented) {
    19. //获取并添加转场视图
    20. UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    21. [transitionContext.containerView addSubview:toView];
    22. //设置动画从右往左出来
    23. toView.x = toView.width;
    24. [UIView animateWithDuration:duration animations:^{
    25. toView.x = 0;
    26. } completion:^(BOOL finished) {
    27. //移除视图
    28. BOOL cancle = [transitionContext transitionWasCancelled];
    29. if (cancle) {
    30. [toView removeFromSuperview];
    31. }
    32. //动画完成后,视图上的事件才能处理
    33. [transitionContext completeTransition:!cancle];
    34. }];
    35. }
    36. //销毁的动画
    37. else
    38. {
    39. //不做处理,而是交给自定义的交互动画去完成
    40. }
    41. }
    42. @end
  • CustomInteractiveTransition
    1. #import <UIKit/UIKit.h>
    2. #import "Singleton.h"
    3.  
    4. @interface CustomInteractiveTransition : NSObject<UIViewControllerInteractiveTransitioning>
    5. SingletonH(CustomInteractiveTransition); //采用单例的方式主要是为了保存交互上下文
    6. //动画进度更新
    7. -(void)updateAnimationProgress:(CGFloat)progress;
    8. //动画完成
    9. -(void)finish;
    10. //动画取消
    11. -(void)cancel;
    12. @end
    1. #import "CustomInteractiveTransition.h"
    2. #import "UIView+Extension.h"
    3.  
    4. @interface CustomInteractiveTransition ()
    5. @property (nonatomic, strong) id<UIViewControllerContextTransitioning> context;
    6. @end
    7.  
    8. @implementation CustomInteractiveTransition
    9. SingletonM(CustomInteractiveTransition);
    10. #pragma mark - UIViewControllerInteractiveTransitioning
    11.  
    12. //开始交互时调用
    13. - (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
    14. //保存上下文
    15. self.context = transitionContext;
    16. //更改视图层级
    17. UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    18. UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    19. [transitionContext.containerView insertSubview:toView belowSubview:fromView];
    20. }
    21. //动画进度更新
    22. -(void)updateAnimationProgress:(CGFloat)progress {
    23. UIView *fromView = [self.context viewForKey:UITransitionContextFromViewKey];
    24. fromView.x = self.context.containerView.width * progress;
    25. }
    26. //动画完成
    27. -(void)finish {

    28. UIView *fromView = [self.context viewForKey:UITransitionContextFromViewKey];
    29. [UIView animateWithDuration:0.2 animations:^{
    30. fromView.x += self.context.containerView.width;
    31. } completion:^(BOOL finished) {
      [fromView removeFromSuperView];
      [self.context completeTransition:finished];
    32. }];
    33. }
    34. //动画取消
    35. -(void)cancel {

    36. UIView *fromView = [self.context viewForKey:UITransitionContextFromViewKey];
    37. [UIView animateWithDuration:0.2 animations:^{
    38. fromView.x = 0;
    39. } completion:^(BOOL finished) {
    40. [fromView removeFromSuperView];
      [self.context cancelInteractiveTransition];
    41. }];
    42. }
    43. @end
  • 在被模态的控制器添加拖拽手势
    1. #import "SecondViewController.h"
    2. #import "CustomInteractiveTransition.h"
    3.  
    4. @interface SecondViewController ()
    5. @end
    6.  
    7. @implementation SecondViewController
    8. - (void)viewDidLoad {
    9. [super viewDidLoad];
    10. self.title = @"secondVc";
    11. self.view.backgroundColor = [UIColor redColor];
    12. [self.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]];
    13. }
    14. -(void)pan:(UIPanGestureRecognizer *)pan {
    15. CGPoint translatedPoint = [pan translationInView:self.view];
    16. CGFloat progress = translatedPoint.x / [UIScreen mainScreen].bounds.size.width;
    17. if (progress < 0) {
    18. return;
    19. }
    20. //拖拽的距离进度比
    21. progress = fabs(progress);
    22. CustomInteractiveTransition *transition = [[CustomInteractiveTransition alloc] init];
    23. switch (pan.state) {
    24. case UIGestureRecognizerStateBegan:
    25. [self dismissViewControllerAnimated:YES completion:nil];
    26. break;
    27. case UIGestureRecognizerStateChanged:
    28. [transition updateAnimationProgress:progress];
    29. break;
    30. case UIGestureRecognizerStateEnded:
    31. {
    32. if (progress > 0.5) {
    33. [transition finish];
    34. }else{
    35. [transition cancel];
    36. }
    37. break;
    38. }
    39. default:
    40. break;
    41. }
    42. }
    43. @end
  • 开始执行,结果如gif图
    1. UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]];
    2. nav.transitioningDelegate = [TransitioningDelegate sharedTransitioningDelegate];//自定义可交互转场动画
    3. nav.modalPresentationStyle = UIModalPresentationFullScreen; //系统模态风格
    4. [self presentViewController:nav animated:YES completion:nil];

 

五、实现一个自定义的导航动画

1、重写导航控制器的协议,返回自定义的导航转场动画,动画实现的方式和modal思想一致,重写的核心协议如下:

  1. //重写导航控制器协议
  2. @protocol UINavigationControllerDelegate <NSObject>
  3. @optional
  4. ................
  5. //返回一个实现了自定义交互动画的对象
  6. - (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
  7. interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController;
  8. //返回一个实现了普通动画的对象
  9. - (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
  10. animationControllerForOperation:(UINavigationControllerOperation)operation
  11. fromViewController:(UIViewController *)fromVC
  12. toViewController:(UIViewController *)toVC;
  13. @end

2、 现在就来自定义一个导航转场动画,步骤如下:

  • 创建一个CustomNavigationTransition类,实现导航控制器的协议
    1. #import <UIKit/UIKit.h>
    2. #import "Singleton.h"
    3. NS_ASSUME_NONNULL_BEGIN
    4. @interface CustomNavigationTransition : NSObject<UINavigationControllerDelegate>
    5. SingletonH(CustomNavigationTransition);
    6. @end
    7. NS_ASSUME_NONNULL_END
    1. #import "CustomNavigationTransition.h"
    2. #import "CustomNavigationAnimation.h"
    3.  
    4. @implementation CustomNavigationTransition
    5. SingletonM(CustomNavigationTransition);
    6. #pragma mark - UINavigationControllerDelegate
    7.  
    8. //返回一个实现了普通动画的对象
    9. - (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
    10. animationControllerForOperation:(UINavigationControllerOperation)operation
    11. fromViewController:(UIViewController *)fromVC
    12. toViewController:(UIViewController *)toVC {
    13. CustomNavigationAnimation *animation = [[CustomNavigationAnimation alloc] init];
    14. animation.operation = operation;
    15. return animation;
    16. }
    17. @end
  • 自定义一个CustomNavigationAnimation类,实现动画协议,重写动画效果
    1. #import <UIKit/UIKit.h>
    2. NS_ASSUME_NONNULL_BEGIN
    3. @interface CustomNavigationAnimation : NSObject<UIViewControllerAnimatedTransitioning>
    4. @property (nonatomic, assign) UINavigationControllerOperation operation;
    5. @end
    6. NS_ASSUME_NONNULL_END
    1. #import "CustomNavigationAnimation.h"
    2. #import "UIView+Extension.h"
    3.  
    4. const CGFloat _duration = 0.5f;
    5. @implementation CustomNavigationAnimation
    6. #pragma mark -<UIViewControllerAnimatedTransitioning>
    7. //动画时间
    8. - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    9. {
    10. return _duration;
    11. }
    12. //设置过渡动画(modal和dismiss的动画都需要在这里处理)
    13. - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
    14. {
    15. // UITransitionContextToViewKey,
    16. // UITransitionContextFromViewKey.
    17. //push
    18. if (self.operation == UINavigationControllerOperationPush) {
    19. //转场视图
    20. UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    21. [transitionContext.containerView addSubview:toView];
    22. //设置动画从右上push进来
    23. toView.x = toView.width;
    24. toView.y = -toView.height;
    25. [UIView animateWithDuration:_duration animations:^{
    26. toView.x = 0;
    27. toView.y = 0;
    28. } completion:^(BOOL finished) {
    29. //移除视图
    30. BOOL cancle = [transitionContext transitionWasCancelled];
    31. if (cancle) {
    32. [toView removeFromSuperview];
    33. }
    34. //动画完成后,视图上的事件才能处理
    35. [transitionContext completeTransition:!cancle];
    36. }];
    37. }
    38. //pop
    39. else if(self.operation == UINavigationControllerOperationPop)
    40. {
    41. //转场视图,更改层级关系
    42. UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    43. UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    44. [transitionContext.containerView insertSubview:toView belowSubview:fromView];
    45. [UIView animateWithDuration:_duration animations:^{
    46. //pop返回右上
    47. fromView.x = fromView.width;
    48. fromView.y = -fromView.height;
    49. } completion:^(BOOL finished) {
    50. //移除视图
    51. BOOL cancle = [transitionContext transitionWasCancelled];
    52. if (!cancle) {
    53. [fromView removeFromSuperview];
    54. }
    55. //动画完成后,视图上的事件才能处理
    56. [transitionContext completeTransition:!cancle];
    57. }];
    58. }
    59. }
    60. @end
  • 开始执行,结果如gif图
    1. //push
    2. ThirdViewController *vc = [[ThirdViewController alloc] init];
    3. self.navigationController.delegate = [CustomNavigationTransition sharedCustomNavigationTransition];
    4. [self.navigationController pushViewController:vc animated:YES];


六、实现一个自定义的标签栏切换动画

1、重写标签栏控制器的协议,返回自定义的标签栏切换转场动画,动画实现的方式和modal思想一致,重写的核心协议如下:

  1. //重写标签栏协议
  2. @protocol UITabBarControllerDelegate <NSObject>
  3. @optional
  4. ......................
  5. //返回一个实现了可交互的标签栏转场动画对象
  6. - (nullable id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController
  7. interactionControllerForAnimationController: (id <UIViewControllerAnimatedTransitioning>)animationController;
  8. //返回一个实现了普通的标签栏转场动画对象
  9. - (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
  10. animationControllerForTransitionFromViewController:(UIViewController *)fromVC
  11. toViewController:(UIViewController *)toVC;
  12. @end

2、 现在就来自定义一个标签转场动画,步骤如下:

  • 创建一个CustomTabBarViewController类,设置代理CustomTabbarTransition实例
    1. //注意:我使用StoryBoard搭建的界面
    2. #import <UIKit/UIKit.h>
    3. NS_ASSUME_NONNULL_BEGIN
    4. @interface CustomTabBarViewController : UITabBarController
    5. @end
    6. NS_ASSUME_NONNULL_END
    1. #import "CustomTabBarViewController.h"
    2. #import "CustomTabbarTransition.h"
    3.  
    4. @interface CustomTabBarViewController ()
    5. @end
    6.  
    7. @implementation CustomTabBarViewController
    8. -(instancetype)initWithCoder:(NSCoder *)coder {
    9. if (self = [super initWithCoder:coder]) {
    10. //设置代理
    11. self.delegate = [CustomTabbarTransition sharedCustomTabbarTransition];
    12. }
    13. return self;
    14. }
    15. - (void)viewDidLoad {
    16. [super viewDidLoad];
    17. }
    18. @end
  • 创建一个CustomTabbarTransition类,实现标签栏控制器的协议
    1. #import <UIKit/UIKit.h>
    2. #import "Singleton.h"
    3. NS_ASSUME_NONNULL_BEGIN
    4. @interface CustomTabbarTransition : NSObject<UITabBarControllerDelegate>
    5. SingletonH(CustomTabbarTransition);
    6. @end
    7. NS_ASSUME_NONNULL_END
    1. #import "CustomTabbarTransition.h"
    2. #import "CustomTabbarAnimation.h"
    3.  
    4. @implementation CustomTabbarTransition
    5. SingletonM(CustomTabbarTransition);
    6. #pragma mark - UITabBarControllerDelegate
    7. //返回一个实现了普通动画的对象
    8. - (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
    9. animationControllerForTransitionFromViewController:(UIViewController *)fromVC
    10. toViewController:(UIViewController *)toVC {
    11. CustomTabbarAnimation *animation = [[CustomTabbarAnimation alloc] init];
    12. return animation;
    13. }
    14. @end
  • 创建一CustomTabbarAnimation类,自定义标签切换动画
    1. #import <UIKit/UIKit.h>
    2. NS_ASSUME_NONNULL_BEGIN
    3. @interface CustomTabbarAnimation : NSObject<UIViewControllerAnimatedTransitioning>
    4.  
    5. @end
    6. NS_ASSUME_NONNULL_END
    1. #import "CustomTabbarAnimation.h"
    2. #import "UIView+Extension.h"
    3.  
    4. const CGFloat _Duration = 0.5f;
    5. @implementation CustomTabbarAnimation
    6. #pragma mark -<UIViewControllerAnimatedTransitioning>
    7. //动画时间
    8. - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    9. {
    10. return _Duration;
    11. }
    12. //设置过渡动画(modal和dismiss的动画都需要在这里处理)
    13. - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
    14. {
    15. // UITransitionContextToViewKey,
    16. // UITransitionContextFromViewKey.
    17. //转场视图
    18. UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    19. UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    20. [transitionContext.containerView addSubview:toView];
    21. [transitionContext.containerView sendSubviewToBack:toView];
    22. //尺寸变化,从原尺寸缩小到点
    23. CGRect finalFrame = CGRectInset(transitionContext.containerView.frame, transitionContext.containerView.frame.size.width/2, transitionContext.containerView.frame.size.height/2);
    24. [UIView animateWithDuration:_Duration animations:^{
    25. fromView.frame = finalFrame;
    26. } completion:^(BOOL finished) {
    27. //移除视图
    28. BOOL cancle = [transitionContext transitionWasCancelled];
    29. if (!cancle) {
    30. [fromView removeFromSuperview];
    31. }
    32. //动画完成后,视图上的事件才能处理
    33. [transitionContext completeTransition:!cancle];
    34. }];
    35. }
    36. @end
  • 开始执行,结果如gif图

 

 

七、总结

好了,这三种常用的转场动画都进行了自定义,当然至于后两种的交互转场动画跟modal实现原理一样,就不介绍了。借用和修改别人的一个图,做个总结吧,如下:

 

原文链接:http://www.cnblogs.com/XYQ-208910/p/11814259.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号