1

我想用 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];
}

这两个功能完美运行

但这是“取消转换”的问题 当我取消转换时它突然消失(我已经尝试过这个问题中的解决方案但这对我不起作用)现在是我的代码:

- (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];
   }];

}

现在我几乎花了整整一个星期的时间,如果你能帮助我,我真的断言。

4

1 回答 1

3

我们可以轻松地将图层的属性从初始值设置为最终值。一般当我们想要实现一个交互动画的时候,我们可以使用 UIView 的动画块来达到这个(Go to final position or back to original position)。但是您不能使用诸如 BackgroundColour、CGPath 之类的属性来执行此操作。

其实我们可以通过CAAnimation来控制流程,控制CALayer的timeoffset。当我们在某个精确位置控制动画过程时。我们怎样才能让它回到动画过程?如果你控制的属性是位置。

    [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 Function 是您要刷新的函数:

-(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;
  }
}

就这样

于 2015-07-25T04:17:44.353 回答