使用CABasicAnimation(CAAnimation)进行自定义交互式转换

时间:2015-07-14 09:55:34

标签: ios objective-c core-animation caanimation ios-animations

我想用UIViewAnimation构建交互式过渡。但是我可以为它设置动画的几层属性。所以我决定使用CAAnimation。 我想更改ViewController的视图掩码,这是代码

-(NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext{
    return 0.5f;
}


-(void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext{

   _transitionContext=transitionContext;
   UIViewController *fromVC=
   [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
   UIViewController *toVC=
   [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
   UIView *containerView=[transitionContext containerView];

   _toVC=toVC;
   _fromVC=fromVC;
   [containerView insertSubview:toVC.view aboveSubview:fromVC.view];

   //Create the BezierPath
   UIBezierPath *initailPath=[UIBezierPath bezierPathWithRect:(CGRect)  {{_cellRect.size.width/2,_cellRect.origin.y+_cellRect.size.height/2},.size= {0.5,0.5}}];
   CGFloat radius;
   CGFloat distance;
   if (fromVC.view.frame.size.width>fromVC.view.frame.size.height) {
       distance=fromVC.view.frame.size.width-_cellRect.origin.x;
       radius=distance>_cellRect.origin.x?distance:_cellRect.origin.x+88;
   }else{
       distance=fromVC.view.frame.size.height-_cellRect.origin.y;
       radius=distance>_cellRect.origin.y?distance:_cellRect.origin.y+88;
   }
   radius=radius*2;
   UIBezierPath *finalPath=[UIBezierPath   bezierPathWithOvalInRect:CGRectInset(_cellRect,
                                                                           -  radius,
                                                                           - radius)];
   _initaialPath=initailPath;
   _finalPath=finalPath;
   //Create a Layer Mask
   _maskLayer=[[CAShapeLayer alloc] init];
   _maskLayer.path=finalPath.CGPath;
   toVC.view.layer.mask=_maskLayer;


   [self animateLayer:_maskLayer withCompletion:^{

       BOOL isComple=![transitionContext transitionWasCancelled];
       if (!isComple) {

           [containerView addSubview:fromVC.view];
           [toVC.view removeFromSuperview];
       }
       [transitionContext completeTransition:isComple];
   }];
  }
-(void)startInteractiveTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext{

   _transitionContext=transitionContext;
   [self animateTransition:transitionContext];
   [self pauseTime:[_transitionContext containerView].layer];

}

这是主要的动画,它完美无需互动。 然后我试着用这些代码控制它:

-(void)updateInteractiveTransition:(CGFloat)percentComplete{
   [_transitionContext updateInteractiveTransition:percentComplete];
   [_transitionContext containerView].layer.timeOffset=_pausedTime + [self transitionDuration:_transitionContext]*percentComplete;
}

-(void)finishInteractiveTransition{

   [_transitionContext finishInteractiveTransition];
   [self resumeTime:[_transitionContext containerView].layer];
}

这两个功能完美无缺

但是这里是问题&#34;取消转换&#34; 当我取消过渡时,它会突然消失 (我已尝试使用此解决方案question但这对我不起作用) 这是我的代码:

- (void)cancelInteractiveTransition {
   //Must Cancel System InteractiveTransition FRIST
   [_transitionContext cancelInteractiveTransition];
   //Then adjust the layer time
   CALayer *maskLayer =[_transitionContext containerView].layer;
   maskLayer.beginTime = CACurrentMediaTime();
   //MOST IMPORTANT
   [UIView animateWithDuration:0.5 animations:^{

       maskLayer.timeOffset=0.0;

   } completion:^(BOOL finished) {

       [[_transitionContext containerView ] addSubview:_fromVC.view];
       [_toVC.view removeFromSuperview];
   }];

}

现在我差不多花了整整一周的时间,如果你可以帮助我,我真的可以预测。

1 个答案:

答案 0 :(得分:3)

我们可以轻松地将图层的属性从初始值设置为最终值。 通常,当我们想要实现交互式动画时,我们可以使用UIView的动画块来达到此目的(转到最终位置或返回到原始位置)。但是你不能用像BackgroundColour,CGPath这样的属性来做这件事。

我们可以通过使用CAAnimation控制进程,并控制 CALayer的时间偏移。当我们在一些精确的位置控制动画过程时。我们怎样才能回到动画过程?如果您控制的财产是位置。

    [UIView animateWithDuration:/*Left time*/ 
                          delay:/*delay time*/          
         usingSpringWithDamping:/*damping*/ 
          initialSpringVelocity:/*speed*/ 
                        options:/*option*/ 
                     animations:^{

                        /*Your animation*/

                   } completion:^(BOOL finished) {

    }];

但它不适合像CGPath或BackgroundColor这样的东西。

我们需要一种驱动动画的方法,苹果支持驱动程序,此驱动程序将调用您指定的功能。它将像iPhone屏幕一样每秒执行60次。 该技术是 CADisplayLink 对象。它是一个计时器对象,允许您的应用程序将绘图与显示的刷新率同步。

然后我得到了解决方案:

if (!_displayLink) {
    _displayLink=[CADisplayLink displayLinkWithTarget:self selector:@selector(animationTick:)];
    [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

AnimationTick功能是您刷新的功能:

-(void)animationTick:(CADisplayLink *)displayLink{

  CALayer *maskLayer=[_transitionContext containerView].layer;
  CGFloat timeOffset=maskLayer.timeOffset;
  timeOffset=MAX(0,timeOffset-_piceDistance);
  maskLayer.timeOffset=timeOffset;
  if (timeOffset==0) {
      displayLink.paused=YES;
  }
}

全部