Search code examples
iosxcodescaletransformationcgaffinetransform

CGAffineTransform scale and translation - jump before animation


I am struggling with an issue regarding CGAffineTransform scale and translation where when I set a transform in an animation block on a view that already has a transform the view jumps a bit before animating.

Example:

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

}

Now when buttonPressed() is called the view jumps to the top left about 10 pixels before starting to animate. I only witnessed this issue with a concat transform, using only a translation transform works fine.

Edit: Since I've done a lot of research regarding the matter I think I should mention that this issue appears regardless of whether or not auto layout is turned on


Solution

  • I ran into the same issue, but couldn't find the exact source of the problem. The jump seems to appear only in very specific conditions: If the view animates from a transform t1 to a transform t2 and both transforms are a combination of a scale and a translation (that's exactly your case). Given the following workaround, which doesn't make sense to me, I assume it's a bug in Core Animation.

    First, I tried using CATransform3D instead of CGAffineTransform.

    Old code:

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

    New code:

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

    The new code should be equivalent to the old one (the fourth parameter is set to 1.0 or 0 so that there is no scaling/translation in z direction), and in fact it shows the same jumping. However, here comes the black magic: In the scale transformation, change the z parameter to anything different from 1.0, like this:

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

    This parameter should have no effect, but now the jump is gone.

    🎩✨