美文网首页
viewController切换动画

viewController切换动画

作者: roy_pub | 来源:发表于2017-06-14 10:47 被阅读128次

    抛出问题

    常用的viewController(VC)切换方式有以下三种:

    • 导航切换
    • 模态切换
    • Tab切换

    VC切换的时候,为了使过度更自然,系统提供了切换动画,例如Push切换会有animated属性。系统提供的动画效果,并不总是能满足我们需求,如果想要自定义切换动画该怎么处理?


    问题猜测

    VC 切换的时候,设置动画效果,默认执行的是系统动画,所以需要告诉程序在什么情况下执行自定义动画,而不是执行系统动画,自定义动画的触发条件是什么?
    确定了自定义动画的触发条件,我们就需要执行自定义动画效果,如何写自定义动画效果?

    • 自定义动画的触发条件是什么?
    • 如何执行自定义动画?

    API分析

    上面绕了一圈,绕出两个问题,先看第一个,自定义动画的触发条件是什么?

    • UIViewControllerTransitioningDelegate
    • UINavigationControllerDelegate
    • UITabBarControllerDelegate

    在切换的时候,系统会向实现了这些接口的对象询问是否使用自定义切换效果。比如自定义Present动画,VC遵守了UIViewControllerTransitioningDelegate协议并实现了协议方法,就会执行自定义动画效果,自定义Push动画效果,遵守UINavigationControllerDelegate协议并实现协议相关方法,就会执行Push的自定义动画。

    找到了自定义动画的触发条件,该如何执行执行自定义动画效果呢?
    接下来隆重登场的是 UIViewControllerAnimatedTransitioning,该协议有两个方法:
    - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

    第一个方法是动画执行周期,第二个方法是执行动画的具体内容,我们可以看一下方法二中的transitionContext,使用该属性可以获取执行动画的相关视图:
    -(UIViewController *)viewControllerForKey:(NSString *)key; 获取VC
    -(UIView *)containerView; 执行动画的容器
    -(CGRect)initialFrameForViewController:(UIViewController *)vc; VC初始位置
    -(CGRect)finalFrameForViewController:(UIViewController *)vc; VC切换后位置
    -(void)completeTransition:(BOOL)didComplete;

    说的比较晦涩,图片为简化流程,图一为整体思路,图二为具体动画过程。


    1.png 2.png

    手势交互

    在使用导航Pop上一级界面的时候,可以在屏幕左侧边缘手势滑动返回。自定义动画如何进行手势操作?
    系统已经为我们提供了相关的方法,UIPercentDrivenInteractiveTransition :
    -(void)updateInteractiveTransition:(CGFloat)percentComplete 更新百分比
    -(void)cancelInteractiveTransition
    –(void)finishInteractiveTransition
    VC添加手势,使用updateInteractiveTransition控制进度。


    应用场景

    两个场景,场景一模拟push和pop动画,场景二模拟presen和dismiss动画。不使用系统动画,使用自定义实现下图动画效果和手势操作。

    3.gif
    以导航的Push为例,讲解一个例子:
    首先有一个从A界面跳转到B界面的Push动画,然后有一个从B界面返回A界面的Pop动画,以及返回的手势操作,系统默认的是左侧滑动返回,我们将手势改为手势操作区域为全屏。
    首先写一个继承NSObject,遵守UIViewControllerAnimatedTransitioning协议的对象PushPopAnimation
    设置动画执行时间为0.3
    -(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
        return 0.3f;
    }```
    Push动画和Pop动画:
    `viewControllerForKey`可以获取相关`VC`,以及`VC`上的子视图。
    `containerView`是执行动画容器,执行动画操作要在容器内进行。
    有了动画容器以及动画对象,下一步就是执行动画效果,Push和Pop主要用到的主要是位移操作。
    
    
    

    -(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
    BOOL isPushing = (_aniStyle == AnimationPushStyle);
    if (isPushing) {
    [self animationPush:transitionContext];
    } else {
    [self animationPop:transitionContext];
    }
    }
    -(void)animationPush:(id<UIViewControllerContextTransitioning>)transitionContext {
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *containView = [transitionContext containerView];
    [containView addSubview:toVC.view];
    CGRect visibleFrame = containView.frame;
    CGRect rightFrame = CGRectMake(visibleFrame.size.width, visibleFrame.origin.y, visibleFrame.size.width, visibleFrame.size.height);
    CGRect leftFrame = CGRectMake(-60, visibleFrame.origin.y, visibleFrame.size.width, visibleFrame.size.height);
    toVC.view.frame = rightFrame;
    fromVC.view.frame = visibleFrame;
    [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
    toVC.view.frame = visibleFrame;
    fromVC.view.frame = leftFrame;
    } completion:^(BOOL finished) {
    if ([transitionContext transitionWasCancelled]) {
    [toVC.view removeFromSuperview];
    }
    [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
    }

    -(void)animationPop:(id<UIViewControllerContextTransitioning>)transitionContext {
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *containView = [transitionContext containerView];
    [containView addSubview:toVC.view];
    [containView bringSubviewToFront:fromVC.view];
    CGRect visibleFrame = containView.frame;
    CGRect rightFrame = CGRectMake(visibleFrame.size.width, visibleFrame.origin.y, visibleFrame.size.width, visibleFrame.size.height);
    [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
    toVC.view.frame = visibleFrame;
    fromVC.view.frame = rightFrame;
    } completion:^(BOOL finished) {
    if ([transitionContext transitionWasCancelled]) {
    [toVC.view removeFromSuperview];
    }
    [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
    }

    添加手势支持继承`UIPercentDrivenInteractiveTransition`
    
    • (instancetype)initWithGestrueForController:(UIViewController *)controller {
      self = [super init];
      if (self) {
      _fullBackVC = controller;
      UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handleGesture:)];
      [_fullBackVC.view addGestureRecognizer:gesture];
      }
      return self;
      }

    • (void)handleGesture:(UIPanGestureRecognizer *)gesture {
      CGPoint translation = [gesture translationInView:gesture.view.superview];
      switch (gesture.state) {
      case UIGestureRecognizerStateBegan:
      self.interacting = YES;
      [_fullBackVC.navigationController popViewControllerAnimated:YES];
      break;
      case UIGestureRecognizerStateChanged: {
      CGFloat dis = translation.x / [UIScreen mainScreen].bounds.size.width;
      dis = fminf(fmaxf(dis, 0.0), 1.0);
      _isComplete = dis >= 0.5;
      [self updateInteractiveTransition:dis];
      break;
      }
      case UIGestureRecognizerStateCancelled:
      case UIGestureRecognizerStateEnded:
      self.interacting = NO;
      if (!self.isComplete || gesture.state == UIGestureRecognizerStateCancelled) {
      [self cancelInteractiveTransition];
      } else {
      [self finishInteractiveTransition];
      }
      break;
      default:
      break;
      }
      }

    #### Demo地址
    [Demo地址](https://github.com/royblog/YGTransitionAnimation)

    相关文章

      网友评论

          本文标题:viewController切换动画

          本文链接:https://www.haomeiwen.com/subject/ioffqxtx.html