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

Как синхронно анимировать UIView и CALayer

Чтобы проиллюстрировать этот вопрос, я создал очень маленький проект Xcode в Github (два класса, загрузка 11kb). Посмотрите на суть здесь или используйте git clone git @gist.github.com: 93982af3b65d2151672e.git.


Пожалуйста, рассмотрите следующий сценарий. Пользовательский вид, называемый 'container view', содержит два маленьких квадрата. Иллюстрируется на этом снимке экрана:

Screenshot of the initial position of the two squares

Синий квадрат - это пример 22x22 pt UIView, а красный квадрат - это 22x22 pt CALayer. Для целей этого вопроса я хочу, чтобы два квадрата "приклеивались" к нижнему правому углу внешнего вида, в то время как я одушевляю рамку этого внешнего вида.

Я изменяю рамку представления контейнера в методе класса UIView animateWithDuration:delay:options:animations:completion: с параметром ослабления по умолчанию типа UIViewAnimationCurveEaseOut.

[UIView animateWithDuration:1.0 delay:0 
                    options:UIViewAnimationCurveEaseOut 
                 animations:^{ _containerView.frame = randomFrame; } 
                 completion:nil];

Примечание. Синий UIView и красный кадр CALayer установлены в переопределенном методе setFrame: представления контейнера, но результаты были бы одинаковыми, если бы я установил синее свойство UIView autoresizingMask в UIView flexible left и верхние поля.

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

Вот несколько скриншотов в анимационной последовательности, которые иллюстрируют асинхронность двух квадратов:

Three screenshots mid-animation where the red square is not in the 'right' place

На вопрос: Есть ли способ синхронизировать два изменения кадров, чтобы красный CALayer и синий UIView двигались с той же продолжительностью анимации и кривой ослабления, придерживаясь друг друга, как если бы они были одним смотреть?

P.S. Разумеется, необходимый визуальный результат двух склеиваемых квадратов может быть достигнут любым количеством способов, например, если оба слоя станут либо CALayer, либо UIView s, но проект, в котором находится реальная проблема, имеет очень законной причиной того, что один из них является CALayer, а другой - UIView.

4b9b3361

Ответ 1

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


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

CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"frame"];
[myAnimation setToValue:[NSValue valueWithCGRect:myNewFrame]];
[myAnimation setFromValue:[NSValue valueWithCGRect:[myLayer frame]]];
[myAnimation setDuration:1.0];
[myAnimation setTimingFunction:[CAMediaTimingFuntion functionWithName: kCAMediaTimingFunctionEaseOut]];

[myLayer setFrame:myNewFrame];
[myLayer addAnimation:myAnimation forKey:@"someKeyForMyAnimation"];

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

Также обратите внимание, что явная анимация не изменяет значения и шапки, которые вы должны добавить анимации и установить новое значение (например, в примере выше)


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

Изменить

Вы не можете легко сгруппировать UIView-анимацию с явной анимацией. Также вы не можете использовать группу анимации (поскольку вы применяете их к разным слоям), но yo может использовать CATransaction:

[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFuntion functionWithName: kCAMediaTimingFunctionEaseOut]];

// Layer animation
CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"frame"];
[myAnimation setToValue:[NSValue valueWithCGRect:myNewFrame]];
[myAnimation setFromValue:[NSValue valueWithCGRect:[myLayer frame]]];

[myLayer setFrame:myNewFrame];
[myLayer addAnimation:myAnimation forKey:@"someKeyForMyAnimation"];

// Outer animation
CABasicAnimation *outerAnimation = [CABasicAnimation animationWithKeyPath:@"frame"];
[outerAnimation setToValue:[NSValue valueWithCGRect:myNewOuterFrame]];
[outerAnimation setFromValue:[NSValue valueWithCGRect:[outerView frame]]];

[[outerView layer] setFrame:myNewOuterFrame];
[[outerView layer] addAnimation:outerAnimation forKey:@"someKeyForMyOuterAnimation"];

[CATransaction commit];

Ответ 2

Если вам нужен фрагмент David Rönnqvist в Swift 3.0, здесь у вас есть его:

    CATransaction.begin()
    CATransaction.setAnimationDuration(1.0)
    CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut))

    // Layer animation
    let myAnimation = CABasicAnimation(keyPath: "frame");
    myAnimation.toValue = NSValue(cgRect: myNewFrame)
    myAnimation.fromValue = NSValue(cgRect: myLayer.frame)

    myLayer.frame = myNewFrame
    myLayer.add(myAnimation, forKey: "someKeyForMyAnimation")

    // Outer animation
    let outerAnimation = CABasicAnimation(keyPath: "frame")
    outerAnimation.toValue = NSValue(cgRect: myNewOuterFrame)
    outerAnimation.fromValue = NSValue(cgRect: outerView.frame)

    outerView.layer.frame = myNewOuterFrame
    outerView.layer.add(outerAnimation, forKey: "someKeyForMyOuterAnimation")

    CATransaction.commit()