Подтвердить что ты не робот

Не удается получить текущую позицию CALayer во время анимации

Я пытаюсь добиться анимации, когда вы удерживаете кнопку, она оживляет блок вниз, а когда вы отпускаете, он оживляет его до исходной позиции, но я не могу получить текущую позицию анимационного блока no от того, что. Вот мой код:

-(IBAction)moveDown:(id)sender{

   CGRect position = [[container.layer presentationLayer] frame];

    [movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)];

    [movePath addLineToPoint:CGPointMake(container.frame.origin.x, 310)];

    CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    moveAnim.path = movePath.CGPath;
    moveAnim.removedOnCompletion = NO;
    moveAnim.fillMode = kCAFillModeForwards;


    CAAnimationGroup *animGroup = [CAAnimationGroup animation];
    animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil];
    animGroup.duration = 2.0;
    animGroup.removedOnCompletion = NO;
    animGroup.fillMode = kCAFillModeForwards;

    [container.layer addAnimation:animGroup forKey:nil];
}
-(IBAction)moveUp:(id)sender{
     CGRect position = [[container.layer presentationLayer] frame];

    UIBezierPath *movePath = [UIBezierPath bezierPath];

    [movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)];

    [movePath addLineToPoint:CGPointMake(container.frame.origin.x, 115)];

    CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    moveAnim.path = movePath.CGPath;
    moveAnim.removedOnCompletion = NO;
    moveAnim.fillMode = kCAFillModeForwards;

    CAAnimationGroup *animGroup = [CAAnimationGroup animation];
    animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil];
    animGroup.duration = 2.0;
    animGroup.removedOnCompletion = NO;
    animGroup.fillMode = kCAFillModeForwards;

    [container.layer addAnimation:animGroup forKey:nil];

}

Но линия

   CGRect position = [[container.layer presentationLayer] frame];

возвращает только место назначения, а не текущую позицию. Мне нужно в основном дать мне текущую позицию контейнера, который оживляет, как только я отпущу кнопку, чтобы я мог выполнить следующую анимацию. То, что у меня сейчас, не работает.

4b9b3361

Ответ 1

Я недостаточно проанализировал ваш код, чтобы быть на 100% уверенным, почему [[container.layer presentationLayer] frame] может не вернуть то, что вы ожидаете. Но я вижу несколько проблем.

Одна очевидная проблема заключается в том, что moveDown: не объявляет movePath. Если movePath является переменной экземпляра, вы, вероятно, захотите очистить ее или создать новый экземпляр каждый раз, когда вызывается moveDown:, но я не вижу, как вы это делаете.

Менее очевидная проблема заключается в том, что (судя по использованию removedOnCompletion и fillMode, несмотря на использование presentationLayer), вы, по-видимому, не понимаете, как работает Core Animation. Это оказывается удивительно распространенным, поэтому простите меня, если я ошибаюсь. В любом случае, читайте дальше, потому что я объясню, как работает Core Animation, а затем как исправить вашу проблему.

В Core Animation объект слоя, с которым вы обычно работаете, является модельным слоем. Когда вы присоединяете анимацию к слою, Core Animation создает копию слоя модели, называемую уровнем представления, и анимация меняет свойства слоя представления с течением времени. Анимация никогда не изменяет свойства слоя модели.

Когда анимация заканчивается и (по умолчанию) удаляется, слой презентации разрушается, и значения свойств слоя модели снова вступают в силу. Таким образом, на экране появляется "привязка назад" к его исходному положению/цвету/независимо.

Обычный, но неправильный способ исправить это - установить анимацию removedOnCompletion на NO и ее fillMode на kCAFillModeForwards. Когда вы это делаете, слой презентации висит вокруг, поэтому нет "привязки назад" на экране. Проблема в том, что теперь у вас есть слой представления, висящий с разными значениями, чем слой модели. Если вы спросите слой модели (или представление, которое им владеет) для значения анимированного свойства, вы получите значение, отличное от того, что на экране. И если вы попытаетесь снова оживить свойство, анимация, вероятно, начнется с неправильного места.

Чтобы анимировать свойство layer и сделать его "stick", вам нужно изменить значение свойства слоя модели, а затем применить анимацию. Таким образом, когда анимация будет удалена, а слой презентации уйдет, слой на экране будет выглядеть точно так же, потому что слой модели имеет те же значения свойств, что и его слой представления, когда анимация закончилась.

Теперь я не знаю, почему вы используете ключевой кадр для анимации прямолинейного движения или почему вы используете анимационную группу. Здесь также нет необходимости. И ваши два метода практически идентичны, поэтому отбросьте общий код:

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y {
    CGPoint fromValue = [layer.presentationLayer position];
    CGPoint toValue = CGPointMake(fromValue.x, y);

    layer.position = toValue;

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    animation.fromValue = [NSValue valueWithCGPoint:fromValue];
    animation.toValue = [NSValue valueWithCGPoint:toValue];
    animation.duration = 2;
    [layer addAnimation:animation forKey:animation.keyPath];
}

Обратите внимание, что мы даем анимации ключ, когда я добавляю его к слою. Поскольку мы каждый раз используем один и тот же ключ, каждая новая анимация заменяет (удаляет) предыдущую анимацию, если предыдущая анимация еще не закончена.

Конечно, как только вы играете с этим, вы обнаружите, что если вы moveUp:, когда moveDown: будет только наполовину закончена, анимация moveUp: окажется на половинной скорости, потому что она все еще имеет продолжительность 2 секунды, но только половина, чтобы путешествовать. Мы должны действительно вычислить продолжительность, основанную на пройденном расстоянии:

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY {
    CGPoint fromValue = [layer.presentationLayer position];
    CGPoint toValue = CGPointMake(fromValue.x, y);

    layer.position = toValue;

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
    animation.fromValue = [NSValue valueWithCGPoint:fromValue];
    animation.toValue = [NSValue valueWithCGPoint:toValue];
    animation.duration = 2.0 * (toValue.y - fromValue.y) / (y - baseY);
    [layer addAnimation:animation forKey:animation.keyPath];
}

Если вам действительно нужна анимация keypath в анимационной группе, ваш вопрос должен показать нам, почему вам нужны эти вещи. Во всяком случае, он тоже работает с этими вещами:

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY {
    CGPoint fromValue = [layer.presentationLayer position];
    CGPoint toValue = CGPointMake(fromValue.x, y);

    layer.position = toValue;

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:fromValue];
    [path addLineToPoint:toValue];

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    animation.path = path.CGPath;
    animation.duration =  2.0 * (toValue.y - fromValue.y) / (y - baseY);

    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.duration = animation.duration;
    group.animations = @[animation];
    [layer addAnimation:group forKey:animation.keyPath];
}

Вы можете найти полный код для моей тестовой программы в этом вопросе. Просто создайте новый проект приложения с одним представлением и замените содержимое ViewController.m содержимым сущности.