preface

I don’t know if you’ve ever had to rotate a CALayer 360 degrees, but if you have, you might try animating the rotation using transform and find… CALayer didn’t move. This article will explain and solve this problem in depth.

transform.rotation

The CABasicAnimation supports the transform. Rotation (keyPath). You can change this value from 0 to 2pi for animation. The transform.rotation is rotating around the z axis. You can also specify which axis to rotate around, for example, the X-axis is transform.rotation.x. This animatable property is perfect for the 360 degree rotation requirement. So the question is, if it can, why can’t I write it this way? I went from a 0 degree transform to a 360 degree transform.

CATransform3D start = CATransform3DMakeRotation(0 * M_PI / 180.0.0.0.1);
CATransform3D end = CATransform3DMakeRotation(2 * M_PI / 180.0.0.0.1);
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
animation.fromValue = [NSValue valueWithCATransform3D:start];
animation.toValue = [NSValue valueWithCATransform3D:end];
animation.duration = 3.3;
Copy the code

Animation interpolation

We know that for the animation to be smooth, we have to interpolate between the from and to that we gave and then render those interpolated frames. So how do these values plug in? We can customize an Animatable property to look at. Below is the code needed to customize the rotateX property of Animatable.

@implementation HTCardLayer

- (void)setRotateX:(CGFloat)rotateX {
    _rotateX = rotateX;
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = 1.0 / 300.0;
    self.transform = CATransform3DRotate(transform, rotateX, 1.0.0);
}

- (void)display {
    CGFloat rotateX = [(HTCardLayer *)self.presentationLayer rotateX];
    NSLog(@"%lf", rotateX);
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = 1.0 / 300.0;
    self.transform = CATransform3DRotate(transform, rotateX, 1.0.0);
}

+ (BOOL)needsDisplayForKey:(NSString *)key {
    if ([key isEqualToString:@"rotateX"]) {
        return YES;
    }
    return [super needsDisplayForKey:key];
}
@end
Copy the code

NeedsDisplayForKey tells the system to refresh the display after rotateX changes, and display is responsible for refreshing the display, because the property values being animated are in the presentationLayer, so we take the latest value of rotateX from the presentationLayer. Here are the values of rotateX printed out during the animation. It’s basically a linear process, because I didn’t set any time function. RotateX is a CGFloat, but what about CATransform3D? How does that change?

0.352071
0.730180
1.101104
1.477982
1.833467
2.189324
2.550581
2.915682
3.273214
3.649389
4.013420
4.376663
4.740999
5.113640
5.483836
5.861515
6.234217
Copy the code

CATransform3D interpolation

I added an Animatable property, customMatrix, to see how attributes of type CATransform3D are interpolated. CATransform3D is actually a 4×4 matrix.

- (void)display {
    CGFloat rotateX = [(HTCardLayer *)self.presentationLayer rotateX];
    CATransform3D customMatrix = [(HTCardLayer *)self.presentationLayer customMatrix];
// NSLog(@"%lf", rotateX);
    NSLog(@"%lf, %lf, %lf, %lf", customMatrix.m11, customMatrix.m12, customMatrix.m13, customMatrix.m14);
    NSLog(@"%lf, %lf, %lf, %lf", customMatrix.m21, customMatrix.m22, customMatrix.m23, customMatrix.m24);
    NSLog(@"%lf, %lf, %lf, %lf", customMatrix.m31, customMatrix.m32, customMatrix.m33, customMatrix.m34);
    NSLog(@"%lf, %lf, %lf, %lf", customMatrix.m41, customMatrix.m42, customMatrix.m43, customMatrix.m44);
    NSLog(@ "-- -- -- -- -- -- -- -- --");
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = 1.0 / 300.0;
    self.transform = CATransform3DRotate(transform, rotateX, 1.0.0);
}

+ (BOOL)needsDisplayForKey:(NSString *)key {
    if ([key isEqualToString:@"rotateX"]) {
        return YES;
    }
    if ([key isEqualToString:@"customMatrix"]) {
        return YES;
    }
    return [super needsDisplayForKey:key];
}
Copy the code

The following is part of the data. I used the matrix rotated around the Z-axis, so only M11, M12, M21 and M22 have data. The other values are the basic values of Identity matrix. It can be seen that M11, M12, M21 and M22 also show linear changes respectively.

0.982547.0.186012.0.000000.0.000000
0.186012.0.982547.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.930553.0.366158.0.000000.0.000000
0.366158.0.930553.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.830170.0.557510.0.000000.0.000000
0.557510.0.830170.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.700345.0.713804.0.000000.0.000000
0.713804.0.700345.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.560556.0.828117.0.000000.0.000000
0.828117.0.560556.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.403126.0.915145.0.000000.0.000000
0.915145.0.403126.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.221203.0.975228.0.000000.0.000000
0.975228.0.221203.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.030679.0.999529.0.000000.0.000000
0.999529.0.030679.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.158010.0.987438.0.000000.0.000000
0.987438.0.158010.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.347984.0.937500.0.000000.0.000000
0.937500.0.347984.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.517222.0.855851.0.000000.0.000000
0.855851.0.517222.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.672144.0.740421.0.000000.0.000000
0.740421.0.672144.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.812617.0.582798.0.000000.0.000000
0.582798.0.812617.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.905049.0.425307.0.000000.0.000000
0.425307.0.905049.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.969663.0.244444.0.000000.0.000000
0.244444.0.969663.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
0.998409.0.056390.0.000000.0.000000
0.056390.0.998409.0.000000.0.000000
0.000000.0.000000.1.000000.0.000000
0.000000.0.000000.0.000000.1.000000
---------
Copy the code

This explains why the 0 to 360 degree animation is simply not executed, since 0 and 360 degree matrices are identical and no interpolation can be calculated.

conclusion

To sum up, if you want to rotate the CALayer 360 degrees, either use the Transform. rotation or customize the Animatable properties.