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

CABasicAnimation не обновляется правильно, когда я обновляю модельный слой

В настоящее время я реализую CABasicAnimation, который анимирует свойство CALayer transform. Теперь, хотя я новичок в Core Animation, мне удалось собрать через различные блоги и статьи, такие как objc.io, что очень плохая идея использовать часто (неправильно) рекомендованный метод для того, чтобы анимации могли использоваться, используя fillMode и removedOnCompletion свойства анимации. Этот метод считается плохой практикой для многих, поскольку он создает несоответствие между слоем модели и уровнем представления, поэтому будущие запросы к одному из этих слоев могут не совпадать с тем, что видит пользователь.

Вместо этого рекомендуемым способом создания анимации является обновление слоя модели одновременно с добавлением анимации к анимированному слою. Однако мне трудно понять, как это работает. Моя анимация проста и выглядит следующим образом:

CATransform3D updatedTransform = [self newTransformWithCurrentTransform];
// Location 1
CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
transformAnimation.duration = 1;
transformAnimation.fromValue = [NSValue valueWithCATransform3D:self.layerBeingAnimated.transform]; // Does not work without this.
transformAnimation.toValue = [NSValue valueWithCATransform3D:updatedTransform];
// Location 2
[self.layerBeingAnimated addAnimation:transformAnimation forKey:kTransformAnimationKey];
// Location 3

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

self.layerBeingAnimated.transform = updatedTransform;

В местоположении 1 слой скачет прямо к newTransform и не оживляет. В местоположении 2 слой анимируется точно так, как я хочу, чтобы он от текущего преобразования до newTransform. В местоположении 3 слой переходит вправо до newTransform, возвращается к старому преобразованию, анимируется правильно из fromValue в newTransform и затем остается на newTransform.

Какая сделка здесь? Какое правильное место для обновления слоя модели и почему эти три местоположения производят такие разные результаты?

Спасибо!

4b9b3361

Ответ 1

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

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

Местоположение 1

В первом месте значение модели обновляется до создания анимации. Как только это будет сделано, свойство transform содержит обновленныйTransform. Это означает, что когда вы читаете преобразование из слоя для параметра fromValue, вы возвращаете updateValue. Это, в свою очередь, означает, что и до, и от значения одинаковы, поэтому вы не можете видеть анимацию.

enter image description here

Одна вещь, которая могла бы заставить это местоположение работать как ожидалось, - это прочитать oldValue перед назначением нового значения, а затем использовать это как fromValue. Это будет выглядеть так, как ожидалось.

// Location 1
CATransform3D oldValue = layer.transform; // read the old value first
layer.transform = updatedTransform;       // then update to the new value

CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
anim.duration  = 1.0;
anim.fromValue = [NSValue valueWithCATransform3D:oldValue];
anim.toValue   = [NSValue valueWithCATransform3D:updatedTransform];

Местоположение 2

Во втором примере значение еще не обновляется, когда вы читаете преобразование для значения from, поэтому fromValue и toValue отличаются. После этого значение модели обновляется до конечного значения. На самом деле существует разница между автономным слоем и опорным слоем, но мы его не видим. Свойство transform на CALayer является анимированным и автоматически выполняет "неявную" анимацию при изменении значения. Это означает, что анимация будет добавлена ​​к слою для ключевого пути "transform". Однако это мнение отключает это поведение, когда изменение происходит за пределами блока анимации, поэтому там нет неявной анимации.

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

enter image description here

Местоположение 3

Это оставляет нам последнее место. В этом случае анимация создается так же, как и выше, с разными значениями Value и toValue. Единственное отличие - это порядок добавления явной анимации и изменение свойства, которое вызывает неявную анимацию. В этом случае неявная анимация добавляется после явной анимации, и оба они запускают (!). Обе анимации фактически выполнялись для местоположения 2, но мы не могли видеть его, потому что после него была добавлена ​​явная (более длинная) анимация.

enter image description here

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

enter image description here

Краткое описание происходящего: синее представление получает только добавленную к нему явную анимацию (которая имеет продолжительность 1 секунду). В оранжевом слое сначала добавлена ​​одна и та же анимация эксплойта, а затем добавлена ​​неявная 0,25-секундная анимация. Ни явные, ни неявные анимации не являются "аддитивными", то есть их toValue и fromValue используются как-есть.

Отказ от ответственности: я не работаю в Apple, и я не видел исходный код для Core Animation, поэтому то, что я собираюсь сказать, - это догадки, основанные на том, как они себя ведут.

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

Если анимация настроена как "аддитивная", она добавит значения представления вместо перезаписи (что является сверхмощным). Даже при добавлении анимации порядок все еще имеет значение. Неаддитивная анимация может прийти позже и перезаписать все это.

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

Где мы должны обновить значение?

В этот момент возникает вопрос: как мы можем запретить добавление двух анимаций и где мы должны обновить значение? Место, где обновляется значение, не мешает двум анимациям (но это может повлиять на то, как выглядит конечный результат).

Чтобы предотвратить добавление двух действий в автономный слой, мы временно отключили все "действия" (более общий термин для анимации):

[CATransaction begin];
[CATransaction setDisableActions:YES]; // actions are disabled for now
layer.transform = updatedTransform;
[CATransaction commit];                // until here

Когда мы делаем это, в слой добавляется только одна анимация, так что работает 2 или 3. Это просто вопрос вкуса. Если вы прочитаете oldValue, вы также можете использовать местоположение 1 (пока действие отключено).

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


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