Пользовательский интерактивный переход с CABasicAnimation(CAAnimation)

Я хочу создать интерактивный переход с помощью 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];
   }];

}

Теперь я почти целую неделю трачу на это. Если вы можете мне помочь, я действительно предикую это.

1 ответ

Мы можем легко анимировать свойство слоя от начального значения до конечного значения. Обычно, когда мы хотим реализовать интерактивную анимацию, мы можем просто использовать блок анимации 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.

Нам нужен способ управления анимацией, Apple поддерживает драйвер, этот драйвер будет вызывать функцию, которую вы указали. И она будет выполнять это 60 раз в секунду, как на экране iPhone. Технология является объектом 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;
  }
}

Это все

Другие вопросы по тегам