Catransformlayer
Catransformlayer is different from ordinary calayer, because it cannot display its own content. Only if there is a transform that has a scoped sublayer does it really exist. Catransformlayer does not plane its sublayers, so it can be used to construct a hierarchical 3D structure, such as my arm example.
We will solve the layer flatness problem by rotating the Camara instead of the sublayertransform in the cube sample code. This is a very good trick, but only scoped on a single object, if your scene contains two cubes, then we cannot rotate them alone with this technique.
So, let's try Catransformlayer, the first question is: In the fifth chapter, we use multiple views to construct our cubes, rather than separate layers. We cannot place a boarding map layer in a layer that does not have a host chart without disrupting the existing view hierarchy. We can create a new UIView subclass on top of Catransformlayer (using the +layerclass method). However, to simplify the case, we just rebuilt a separate layer instead of using the view. This means that we cannot display buttons and labels on the cube surface as in the fifth chapter, but we do not have this feature now.
Listing 6.5 is the code. We place the cube in the same basic logic that we used in chapter fifth. But it's not as straightforward as before to add the façade to the host layer of the container view, we put them in a catransformlayer to create a separate cube object, and then put two such cubes into the container. We randomly stain them to distinguish them from each other, so that they are not separated by labels or light. Figure 6.5 shows the result of the operation.
Listing 6.5 assembling a 3D layer system with Catransformlayer
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *containerView;
@end
@implementation ViewController
- (CALayer *)faceWithTransform:(CATransform3D)transform
{
//create cube face layer
CALayer *face = [CALayer layer];
face.frame = CGRectMake(-50, -50, 100, 100);
//apply a random color
CGFloat red = (rand() / (double)INT_MAX);
CGFloat green = (rand() / (double)INT_MAX);
CGFloat blue = (rand() / (double)INT_MAX);
face.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
//apply the transform and return
face.transform = transform;
return
face;
}
- (CALayer *)cubeWithTransform:(CATransform3D)transform
{
//create cube layer
CATransformLayer *cube = [CATransformLayer layer];
//add cube face 1
CATransform3D ct = CATransform3DMakeTranslation(0, 0, 50);
[cube addSublayer:[self faceWithTransform:ct]];
//add cube face 2
ct = CATransform3DMakeTranslation(50, 0, 0);
ct = CATransform3DRotate(ct, M_PI_2, 0, 1, 0);
[cube addSublayer:[self faceWithTransform:ct]];
//add cube face 3
ct = CATransform3DMakeTranslation(0, -50, 0);
ct = CATransform3DRotate(ct, M_PI_2, 1, 0, 0);
[cube addSublayer:[self faceWithTransform:ct]];
//add cube face 4
ct = CATransform3DMakeTranslation(0, 50, 0);
ct = CATransform3DRotate(ct, -M_PI_2, 1, 0, 0);
[cube addSublayer:[self faceWithTransform:ct]];
//add cube face 5
ct = CATransform3DMakeTranslation(-50, 0, 0);
ct = CATransform3DRotate(ct, -M_PI_2, 0, 1, 0);
[cube addSublayer:[self faceWithTransform:ct]];
//add cube face 6
ct = CATransform3DMakeTranslation(0, 0, -50);
ct = CATransform3DRotate(ct, M_PI, 0, 1, 0);
[cube addSublayer:[self faceWithTransform:ct]];
//center the cube layer within the container
CGSize containerSize = self.containerView.bounds.size;
cube.position = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
//apply the transform and return
cube.transform = transform;
return
cube;
}
- (void)viewDidLoad
{?
[
super
viewDidLoad];
//set up the perspective transform
CATransform3D pt = CATransform3DIdentity;
pt.m34 = -1.0 / 500.0;
self.containerView.layer.sublayerTransform = pt;
//set up the transform for cube 1 and add it
CATransform3D c1t = CATransform3DIdentity;
c1t = CATransform3DTranslate(c1t, -100, 0, 0);
CALayer *cube1 = [self cubeWithTransform:c1t];
[self.containerView.layer addSublayer:cube1];
//set up the transform for cube 2 and add it
CATransform3D c2t = CATransform3DIdentity;
c2t = CATransform3DTranslate(c2t, 100, 0, 0);
c2t = CATransform3DRotate(c2t, -M_PI_4, 1, 0, 0);
c2t = CATransform3DRotate(c2t, -M_PI_4, 0, 1, 0);
CALayer *cube2 = [self cubeWithTransform:c2t];
[self.containerView.layer addSublayer:cube2];
}
@end
Cagradientlayer
Cagradientlayer is used to generate two or more color smoothing gradients. It is also possible to copy a cagradientlayer with the core graphics and draw the content to a normal layer, but the real benefit of cagradientlayer is that it uses hardware acceleration.
Base gradient
We'll start with a simple red-to-blue diagonal gradient (see listing 6.6). These gradient colors are placed in an array and assigned to the Colors property. This array member accepts a value of type cgcolorref (not derived from nsobject), so we use bridge transformation to ensure that the compilation is normal.
Cagradientlayer also have StartPoint and endpoint properties, and they determine the direction of the gradient. These two parameters are defined in the unit coordinate system, so the upper-left coordinate is {0, 0}, and the lower-right coordinate is {1, 1}. Code Run result 6.6
Listing 6.6 A simple diagonal gradient of two colors
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *containerView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[
super
viewDidLoad];
//create gradient layer and add it to our container view
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.containerView.bounds;
[self.containerView.layer addSublayer:gradientLayer];
//set gradient colors
gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor blueColor].CGColor];
//set gradient start and end points
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(1, 1);
}
@end
Multiple gradients
If you want, the colors property can contain many colors, so creating a rainbow-like multi-gradient is easy. By default, these colors are uniformly rendered spatially, but we can use the Locations property to adjust the space. The Locations property is an array of floating-point values (in NSNumber wrapper). These floating-point numbers define the position of each of the different colors in the colors attribute, which is also calibrated in the unit coordinate system. 0.0 represents the beginning of the gradient, and 1.0 represents the end.
The locations array is not mandatory, but if you assign it you must make sure that the locations array size and the colors array size must be the same, otherwise you will get a blank gradient.
Listing 6.7 shows a code transformation based on the diagonal gradient of listing 6.6. Now it becomes a gradient from red to yellow and finally to green. The locations array specifies 0.0,0.25 and 0.5 three values, so the three gradients are a bit like squeezing in the upper-left corner. (6.7).
Listing 6.7 using locations on a gradient
- (void)viewDidLoad {
[
super
viewDidLoad];
//create gradient layer and add it to our container view
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.containerView.bounds;
[self.containerView.layer addSublayer:gradientLayer];
//set gradient colors
gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id [UIColor yellowColor].CGColor, (__bridge id)[UIColor greenColor].CGColor];
//set locations
gradientLayer.locations = @[@0.0, @0.25, @0.5];
//set gradient start and end points
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(1, 1);
}
Ca*layer (Catransformlayer--cagradientlayer)