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

Objective-C - CABasicAnimation применение изменений после анимации?

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

Чтобы добиться этого, я слушаю вызов делегата моего CAAnimationGroup, и как только он вызван, я удаляю представление изображения из супервизора.

Проблема заключается в том, что иногда изображение мигает в исходном местоположении перед удалением из супервизора. Какой лучший способ избежать такого поведения?

CAAnimationGroup *animGroup = [CAAnimationGroup animation];
    animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, opacityAnim, nil];
    animGroup.duration = .5;
    animGroup.delegate = self;
    [imageView.layer addAnimation:animGroup forKey:nil];
4b9b3361

Ответ 1

Когда вы добавляете анимацию в слой, анимация не изменяет свойства слоя. Вместо этого система создает копию слоя. Первоначальный слой называется слоем модели, а дубликат называется уровнем представления. Свойства слоя представления изменяются по мере продвижения анимации, но свойства слоя модели остаются неизменными.

При удалении анимации система разрушает слой презентации, оставляя только слой модели, а затем свойства слоя модели управляют тем, как нарисован слой. Поэтому, если свойства модельного слоя не соответствуют конечным анимированным значениям свойств слоя представления, слой будет мгновенно reset до его появления перед анимацией.

Чтобы исправить это, вам нужно установить свойства слоя модели на конечные значения анимации, а затем добавить анимацию к слою. Вы хотите сделать это в этом порядке, потому что изменение свойства слоя может добавить неявную анимацию для свойства, что противоречит анимации, которую вы хотите явно добавить. Вы хотите, чтобы ваша явная анимация перекрывала неявную анимацию.

Итак, как вы все это делаете? Основной рецепт выглядит следующим образом:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:myLayer.position];
layer.position = newPosition; // HERE I UPDATE THE MODEL LAYER PROPERTY
animation.toValue = [NSValue valueWithCGPoint:myLayer.position];
animation.duration = .5;
[myLayer addAnimation:animation forKey:animation.keyPath];

Я не использовал анимационную группу, поэтому не знаю точно, что вам нужно изменить. Я просто добавляю каждую анимацию отдельно к слою.

Мне также проще использовать метод +[CATransaction setCompletionBlock:] для установки обработчика завершения для одной или нескольких анимаций вместо того, чтобы пытаться использовать делегат анимации. Вы устанавливаете блок завершения транзакции, затем добавляете анимацию:

[CATransaction begin]; {
    [CATransaction setCompletionBlock:^{
        [self.imageView removeFromSuperview];
    }];
    [self addPositionAnimation];
    [self addScaleAnimation];
    [self addOpacityAnimation];
} [CATransaction commit];

Ответ 2

CAAnimations удаляются автоматически по завершении. Существует свойство removedOnCompletion, которое контролирует это. Вы должны установить значение NO.

Кроме того, есть что-то известное как fillMode, которое контролирует поведение анимации до и после его продолжительности. Это свойство объявлено на CAMediaTiming (которое соответствует CAAnimation). Вы должны установить значение kCAFillModeForwards.

С обоими этими изменениями анимация должна сохраняться после ее завершения. Однако я не знаю, нужно ли изменять их в группе или отдельных анимациях внутри группы или обоих.

Ответ 3

Вот пример в Swift, который может помочь кому-то

Это анимация на уровне градиента. Он оживляет свойство .locations.

Критическая точка как ответ @robMayoff полностью объясняет, что:

Удивительно, но когда вы делаете анимацию слоя, вы фактически устанавливаете окончательное значение, прежде чем начать анимацию!

Ниже приведен хороший пример, поскольку анимация повторяется бесконечно.

Когда анимация повторяется бесконечно, вы иногда увидите "вспышку" между анимациями, если вы сделаете классическую ошибку "забыв установить значение, прежде чем его оживить!"

var previousLocations: [NSNumber] = []
...

func flexTheColors() { // "flex" the color bands randomly

    let oldValues = previousTargetLocations
    let newValues = randomLocations()
    previousTargetLocations = newValues

    // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!"
    theLayer.locations = newValues

    // AND NOW ANIMATE:
    CATransaction.begin()

    // and by the way, this is how you endlessly animate:
    CATransaction.setCompletionBlock{ [weak self] in
        if self == nil { return }
        self?.animeFlexColorsEndless()
    }

    let a = CABasicAnimation(keyPath: "locations")
    a.isCumulative = false
    a.autoreverses = false
    a.isRemovedOnCompletion = true
    a.repeatCount = 0

    a.fromValue = oldValues
    a.toValue = newValues

    a.duration = (2.0...4.0).random()

    theLayer.add(a, forKey: nil)
    CATransaction.commit()
}

Следующее может помочь прояснить что-то для новых программистов. Обратите внимание, что в моем коде я делаю это:

    // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!"
    theLayer.locations = newValues

    // AND NOW ANIMATE:
    CATransaction.begin()
    ...set up the animation...
    CATransaction.commit()

однако в примере кода в другом ответе он выглядит так:

    CATransaction.begin()
    ...set up the animation...
    // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!"
    theLayer.locations = newValues
    CATransaction.commit()

Относительно положения строки кода, где вы "устанавливаете значения, перед анимацией!"..

На самом деле отлично, чтобы эта строка фактически "внутри" начиналась с строк кода. Пока вы делаете это до .commit().

Я упоминаю только об этом, так как это может запутать новых аниматоров.