Цель-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];
3 59

3 ответа:

при добавлении анимации в слой, анимация не изменяет свойства слоя. Вместо этого, система создает копию слоя. Исходный слой называется слоем модели,а дубликат-слоем представления. Свойства слоя представления изменяются по мере развития анимации, но свойства слоя модели остаются неизменными.

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

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

Так как же вы все это делаете? Основной рецепт выглядит так:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:myLayer.position];
layer.position = newPosition; // HERE I UPDATE THE MODEL LAYER'S 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];

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

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

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

вот пример в 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().

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