CGAffineTransform scale и перевод - прыжок перед анимацией

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

Пример:

// somewhere in view did load or during initialization
var view = UIView()
view.frame = CGRectMake(0,0,100,100)
var scale = CGAffineTransformMakeScale(0.8,0.8)
var translation = CGAffineTransformMakeTranslation(100,100)
var concat = CGAffineTransformConcat(translation, scale)
view.transform = transform

// called sometime later
func buttonPressed() {
    var secondScale = CGAffineTransformMakeScale(0.6,0.6)
    var secondTranslation = CGAffineTransformMakeTranslation(150,300)
    var secondConcat = CGAffineTransformConcat(secondTranslation, secondScale)
    UIView.animateWithDuration(0.5, animations: { () -> Void in 
         view.transform = secondConcat
    })

}

Теперь, когда buttonPressed() называется просмотром, перед тем, как начать анимацию, переходите в левый верхний угол около 10 пикселей. Я только видел эту проблему с преобразованием concat, используя только трансляцию трансляции. Прекрасно.

Изменить: Поскольку я провел много исследований по этому вопросу, я думаю, что я должен упомянуть, что эта проблема появляется независимо от того, включена ли автоматическая компоновка

Ответы

Ответ 1

Я столкнулся с той же проблемой, но не смог найти точный источник проблемы. Кажется, что прыжок появляется только в очень специфических условиях: если представление анимируется из преобразования t1 в преобразование t2, и оба преобразования представляют собой комбинацию масштаба и перевода (это именно ваш случай). Учитывая следующее обходное решение, которое для меня не имеет смысла, я предполагаю, что это ошибка в Core Animation.

Сначала я попытался использовать CATransform3D вместо CGAffineTransform.

Старый код:

var transform = CGAffineTransformIdentity
transform = CGAffineTransformScale(transform, 1.1, 1.1)
transform = CGAffineTransformTranslate(transform, 10, 10)
view.layer.setAffineTransform(transform)

Новый код:

var transform = CATransform3DIdentity
transform = CATransform3DScale(transform, 1.1, 1.1, 1.0)
transform = CATransform3DTranslate(transform, 10, 10, 0)
view.layer.transform = transform

Новый код должен быть эквивалентен старому (четвертый параметр установлен на 1.0 или 0, так что в направлении z нет масштабирования/перевода), и на самом деле он показывает тот же прыжок, Однако здесь идет черная магия: в преобразовании шкалы измените параметр z на что-либо, отличное от 1.0, например:

transform = CATransform3DScale(transform, 1.1, 1.1, 1.01)

Этот параметр не должен иметь никакого эффекта, но теперь переход исчез.

🎩✨

Ответ 2

Источником проблемы является отсутствие перспективной информации для преобразования.

Вы можете добавить перспективную информацию, изменяющую свойство m34 вашего 3D-преобразования

var transform = CATransform3DIdentity
transform.m34 = 1.0 / 200 //your own perspective value here
transform = CATransform3DScale(transform, 1.1, 1.1, 1.0)
transform = CATransform3DTranslate(transform, 10, 10, 0)
view.layer.transform = transform

Ответ 3

Похоже, что внутренняя ошибка анимации Apple UIView. Когда Apple интерполирует CGAffineTransform изменения между двумя значениями для создания анимации, он должен выполнить следующие шаги:

  • Извлечение трансляции, масштабирования и вращения
  • Интерполяция значений извлеченных значений начинается с конца
  • Соберите CGAffineTransform для каждого шага интерполяции

Сборка должна быть в следующем порядке:

  • Перевод
  • Масштабирование
  • Вращение

Но похоже, что Apple делает перевод после масштабирования и вращения. Эта ошибка должна быть исправлена ​​Apple.

Ответ 4

Вместо CGAffineTransformMakeScale() и CGAffineTransformMakeTranslation(), которые создают преобразование, основанное на CGAffineTransformIdentity (в основном без преобразования), вы хотите масштабировать и транслировать на основе текущего преобразования данных с использованием CGAffineTransformScale() и CGAffineTransformTranslate(), которые запускаются с существующим преобразованием.