ios xcode scale transformation cgaffinetransform

ios - Escala y traducción de CGAffineTransform-Saltar antes de la animación



xcode scale (5)

En lugar de CGAffineTransformMakeScale () y CGAffineTransformMakeTranslation (), que crean una transformación basada en CGAffineTransformIdentity (básicamente ninguna transformación), desea escalar y traducir en función de la transformación actual de la vista utilizando CGAffineTransformScale () y CGAffineTransformTranslate (), que comienza con la existente transformar.

Estoy luchando con un problema relacionado con la escala y la traducción de CGAffineTransform, donde cuando configuro una transformación en un bloque de animación en una vista que ya tiene una transformación, la vista salta un poco antes de animar.

Ejemplo:

// 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 }) }

Ahora, cuando se llama buttonPressed (), la vista salta a la parte superior izquierda unos 10 píxeles antes de comenzar a animar. Solo presencié este problema con una transformación concat, usar solo una transformación de traducción funciona bien.

Edit: Ya que he investigado mucho sobre el tema, creo que debería mencionar que este problema aparece independientemente de si el diseño automático está activado o no.


La fuente del problema es la falta de información en perspectiva para la transformación.

Puede agregar información de perspectiva modificando la propiedad m34 de su transformación 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


Me encontré con el mismo problema, pero no pude encontrar la fuente exacta del problema. El salto parece aparecer solo en condiciones muy específicas: si la vista se anima desde una transformación t1 a una transformación t2 y ambas transformaciones son una combinación de una escala y una traducción (ese es exactamente su caso). Dada la siguiente solución, que no tiene sentido para mí, asumo que es un error en Core Animation.

Primero, intenté usar CATransform3D lugar de CGAffineTransform .

Código antiguo:

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

Nuevo código:

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

El nuevo código debe ser equivalente al anterior (el cuarto parámetro se establece en 1.0 o 0 para que no haya escalado / traducción en la dirección z ), y de hecho muestra el mismo salto. Sin embargo, aquí viene la magia negra: en la transformación de escala, cambia el parámetro z a algo diferente de 1.0 , como esto:

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

Este parámetro no debería tener ningún efecto, pero ahora el salto se ha ido.

🎩✨


Parece que la animación de Apple UIView error interno. Cuando Apple interpola los cambios de CGAffineTransform entre dos valores para crear la animación, debe realizar los siguientes pasos:

  • Extraer traslación, escala y rotación.
  • Interpolar los valores extraídos de principio a fin.
  • Ensamble CGAffineTransform para cada paso de interpolación

El montaje debe ser en el siguiente orden:

  • Traducción
  • Escalada
  • Rotación

Pero parece que Apple hace la traducción después de la escala y la rotación. Este error debe ser arreglado por Apple.


No sé por qué, pero este código puede funcionar.

actualizar:

Combino con éxito escala, traslación y rotación, desde cualquier estado de transformación a cualquier nuevo estado de transformación.

Creo que la transformación se reinterpreta al comienzo de la animación.

el ancla de la transformación de inicio se considera en la nueva transformación, y luego la convertimos a la transformación antigua.

self.v = UIView(frame: CGRect(x: 50, y: 50, width: 50, height: 50)) self.v?.backgroundColor = .blue self.view.addSubview(v!) func buttonPressed() { let view = self.v! let m1 = view.transform let tempScale = CGFloat(arc4random()%10)/10 + 1.0 let tempRotae:CGFloat = 1 let m2 = m1.translatedBy(x: CGFloat(arc4random()%30), y: CGFloat(arc4random()%30)).scaledBy(x: tempScale, y: tempScale).rotated(by:tempRotae) self.animationViewToNewTransform(view: view, newTranform: m2) } func animationViewToNewTransform(view: UIView, newTranform: CGAffineTransform) { // 1. pointInView.apply(view.transform) is not correct point. // the real matrix is mAnchorToOrigin.inverted().concatenating(m1).concatenating(mAnchorToOrigin) // 2. animation begin trasform is relative to final transform in final transform coordinate // anchor and mAnchor let normalizedAnchor0 = view.layer.anchorPoint let anchor0 = CGPoint(x: normalizedAnchor0.x * view.bounds.width, y: normalizedAnchor0.y * view.bounds.height) let mAnchor0 = CGAffineTransform.identity.translatedBy(x: anchor0.x, y: anchor0.y) // 0->1->2 //let origin = CGPoint(x: 0, y: 0) //let m0 = CGAffineTransform.identity let m1 = view.transform let m2 = newTranform // rotate and scale relative to anchor, not to origin let matrix1 = mAnchor0.inverted().concatenating(m1).concatenating(mAnchor0) let matrix2 = mAnchor0.inverted().concatenating(m2).concatenating(mAnchor0) let anchor1 = anchor0.applying(matrix1) let mAnchor1 = CGAffineTransform.identity.translatedBy(x: anchor1.x, y: anchor1.y) let anchor2 = anchor0.applying(matrix2) let txty2 = CGPoint(x: anchor2.x - anchor0.x, y: anchor2.y - anchor0.y) let txty2plusAnchor2 = CGPoint(x: txty2.x + anchor2.x, y: txty2.y + anchor2.y) let anchor1InM2System = anchor1.applying(matrix2.inverted()).applying(mAnchor0.inverted()) let txty2ToM0System = txty2plusAnchor2.applying(matrix2.inverted()).applying(mAnchor0.inverted()) let txty2ToM1System = txty2ToM0System.applying(mAnchor0).applying(matrix1).applying(mAnchor1.inverted()) var m1New = m1 m1New.tx = txty2ToM1System.x + anchor1InM2System.x m1New.ty = txty2ToM1System.y + anchor1InM2System.y view.transform = m1New UIView.animate(withDuration: 1.4) { view.transform = m2 } }

También intento la solución zScale, parece que también funciona si se establece zScale non-1 en la primera transformación o en cada transformación

let oldTransform = view.layer.transform let tempScale = CGFloat(arc4random()%10)/10 + 1.0 var newTransform = CATransform3DScale(oldTransform, tempScale, tempScale, 1.01) newTransform = CATransform3DTranslate(newTransform, CGFloat(arc4random()%30), CGFloat(arc4random()%30), 0) newTransform = CATransform3DRotate(newTransform, 1, 0, 0, 1) UIView.animate(withDuration: 1.4) { view.layer.transform = newTransform }