Je suis aux prises avec un problème relatif à l'échelle et à la traduction de CGAffineTransform, où lorsque je définis une transformation dans un bloc d'animation sur une vue comportant déjà une transformation, la vue saute un peu avant l'animation.
Exemple:
// 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
})
}
Désormais, lorsque buttonPressed () est appelée, la vue passe en haut à gauche à environ 10 pixels avant de commencer à animer. J'ai seulement été témoin de ce problème avec une transformation de concat, utiliser uniquement une transformation de traduction fonctionne bien.
Edit: Depuis que j'ai fait beaucoup de recherches sur le sujet, je pense que je devrais mentionner que ce problème apparaît que la mise en page automatique soit activée ou non
J'ai rencontré le même problème, mais je n'ai pas pu trouver la source exacte du problème. Le saut semble apparaître uniquement dans des conditions très spécifiques: Si la vue s'anime d'une transformation t1
à une transformation t2
et que les deux transformations associent une échelle et une traduction (c'est exactement ce que vous faites). Compte tenu de la solution de contournement suivante, ce qui n’a aucun sens pour moi, je suppose que c’est un bogue dans Core Animation.
J'ai d'abord essayé d'utiliser CATransform3D
au lieu de CGAffineTransform
.
Ancien code:
var transform = CGAffineTransformIdentity
transform = CGAffineTransformScale(transform, 1.1, 1.1)
transform = CGAffineTransformTranslate(transform, 10, 10)
view.layer.setAffineTransform(transform)
Nouveau code:
var transform = CATransform3DIdentity
transform = CATransform3DScale(transform, 1.1, 1.1, 1.0)
transform = CATransform3DTranslate(transform, 10, 10, 0)
view.layer.transform = transform
Le nouveau code doit être équivalent à l'ancien (le quatrième paramètre est défini sur 1.0
ou 0
de sorte qu'il n'y ait pas de redimensionnement/de traduction dans la direction z
), et montre en fait le même saut. Cependant, voici la magie noire: dans la transformation d'échelle, remplacez le paramètre z
par un élément différent de 1.0
, comme ceci:
transform = CATransform3DScale(transform, 1.1, 1.1, 1.01)
Ce paramètre ne devrait avoir aucun effet, mais maintenant le saut est parti.
????
Ressemble au bogue interne de l'animation Apple UIView. Lorsque Apple interpole CGAffineTransform
entre deux valeurs pour créer une animation, procédez comme suit:
CGAffineTransform
pour chaque étape d'interpolationL'assemblage devrait être dans l'ordre suivant:
Mais on dirait que Apple fait la traduction après la mise à l'échelle et la rotation. Ce bogue devrait être corrigé par Apple.
Je ne sais pas pourquoi, mais ce code peut fonctionner
mettre à jour:
Je réussis à combiner la mise à l'échelle, la traduction et la rotation ensemble, d'un état de transformation à un nouvel état de transformation.
Je pense que la transformation est réinterprétée au début de l'animation.
l'ancre de la transformation de début est prise en compte dans la nouvelle transformation, puis nous la convertissons en ancienne transformation.
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
}
}
J'essaie également la solution zScale. Il semble également fonctionner si vous définissez zScale non-1 à la première transformation ou à chaque transformation.
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
}
La source du problème est le manque d'informations de perspective sur la transformation.
Vous pouvez ajouter des informations de perspective en modifiant la propriété m34
de votre transformation 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
Au lieu de CGAffineTransformMakeScale () et CGAffineTransformMakeTranslation (), qui créent une transformation à partir de CGAffineTransformIdentity (essentiellement aucune transformation), vous souhaitez redimensionner et traduire en fonction de la transformation actuelle de la vue à l'aide de CGAffineTransformScale () et CGAffineTransformlate (), qui démarre avec la transformation en cours. transformer.