From CATransform3D to CGAffineTransform

core-animation, core-graphics

Solution

It's possible convert a `CATransform3D` to a `CGAffineTransform`, but you will lose some capabilities. I found it useful to convert the aggregate transform of a layer and its ancestors into a `CGAffineTransform` so I could render it with Core Graphics. The constraints are:

- Your input will be treated as flat in the XY plane

- Your output will be treated as flat in the XY plane too

- Perspective / foreshortening from `.m34` will be neutralized

If that sounds OK for your purposes:

    // m13, m23, m33, m43 are not important since the destination is a flat XY plane.
    // m31, m32 are not important since they would multiply with z = 0.
    // m34 is zeroed here, so that neutralizes foreshortening. We can't avoid that.
    // m44 is implicitly 1 as CGAffineTransform's m33.
    CATransform3D fullTransform = <your 3D transform>
    CGAffineTransform affine = CGAffineTransformMake(fullTransform.m11, fullTransform.m12, fullTransform.m21, fullTransform.m22, fullTransform.m41, fullTransform.m42);

You will want to do all your work in 3D transforms first, say by concatenating from your superlayers, and then finally convert the aggregate `CATransform3D` to a `CGAffineTransform`. Given that layers are flat to begin with and render onto a flat target, I found this very suitable since my 3D rotations became 2D shears. I also found it acceptable to sacrifice foreshortening. There's no way around that because affine transforms have to preserve parallel lines.

To render a 3D-transformed layer using Core Graphics, for instance, you might concatenate the transforms (respecting anchor points!), then convert to affine, and finally:

    CGContextSaveGState(context);
    CGContextConcatCTM(context, affine);
    [layer renderInContext:context];
    CGContextRestoreGState(context);

Problem

I'm using the following function to apply a pulse effect to a view ``` - (void)pulse { CATransform3D trasform = CATransform3DScale(self.layer.transform, 1.15, 1.15, 1); trasform = CATransform3DRotate(trasform, angle, 0, 0, 0); CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"]; animation.toValue = [NSValue valueWithCATransform3D:trasform]; animation.autoreverses = YES; animation.duration = 0.3; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; animation.repeatCount = 2; [self.layer addAnimation:animation forKey:@"pulseAnimation"]; } ``` I would like to obtain the same result using the CGAffineTransform self.transform instead of the CATransform3D self.layer.transform. Is this possible?

Original source