关于CATransformLayer的介绍请参考专用图层。
什么时候使用CATransformLayer?
Core Animation中的3D绘画介绍(第一部分)中有介绍WORK WITH CATRANSFORMLAYER,大致的翻译下:
坦白的讲,CALayer并不是3D层级的root的合适选择。函数E_multiplePlanesZAxis将会告诉我们为什么会是这样。
在这个场景中,我们在相同的X,Y坐标系中创建4个平面(plane),但是却对应有不同的Z坐标。
purplePlane是最靠近你的view,而YellowPlane是最远的。
//给planes应用变换
t = CATransform3DIdentity;
t = CATransform3DTranslate(t, 0, 0, -10);
purplePlane.transform = t;
t = CATransform3DIdentity;
t = CATransform3DTranslate(t, 0, 0, -50);
redPlane.transform = t;
t = CATransform3DIdentity;
t = CATransform3DTranslate(t, 0, 0, -90);
orangePlane.transform = t;
t = CATransform3DIdentity;
t = CATransform3DTranslate(t, 0, 0, -130);
yellowPlane.transform = t;
在创建这些planes之前,给container 应用一个rotation变换(这里的container是一个CALayer)。
//给container应用变化
CATransform3D t = CATransform3DIdentity;
t.m34 = 1.0/-500;
t = CATransform3DRotate(t, 80.0f * M_PI / 180.0f, 0, 1, 0);
container.transform = t;
这是因为CALayer不能够管理3D层级的深度,它只是flattens the scene to a single Z level.
为了修正这个问题,获得正确的结果,要选择CATransformLayers作为root object。
在函数F_multiplePlanesZAxis
中修改这个问题:
//Create the container as a CATransformLayer
CATransformLayer *container = [CATransformLayer layer];
container.frame = CGRectMake(0, 0, 640, 300);
[self.view.layer addSublayer:container];
CATransformLayer是一个特殊的layer。与CALayers不同的是,只有它的子layer才会被渲染,其它的属性,比如backgroundColor、contents、border等等,都会被忽略。
[Core Animation中的3D绘画介绍(第二部分)](http://www.thinkandbuild.it/introduction-to-3d-drawing-in-core-animation-part-2/)
在这个教程中,创建一个类似旋转木马类似的场景,可以通过拖动手势来交互。效果如下:
We go 3D!
为了创建carousel,我们需要创建3D层级,使用CATransformLayer作为层级的root是个好方法。
The planes平面
Carousel由一系列平面组成。我们使用CAGradientLayer来描述这些对象,它是CALayer的子类,让我们可以使用渐变的颜色来定义背景颜色。
plane需要移动和旋转来围成一个圆形。
手势
使用了一个手势,来追踪用户的的动作,把手势的数据转为角度值,来执行carousel的旋转。
打开ViewController.m,从viewDidLoad:函数开始:
- (void)viewDidLoad
{
[super viewDidLoad];
//初始化TransformLayer
transformLayer = [CATransformLayer layer];
transformLayer.frame = self.view.bounds;
[self.view.layer addSublayer:transformLayer];
angle = 0;
XPanOffset = 0;
//创建5个planes
[self addPlane];
[self addPlane];
[self addPlane];
[self addPlane];
[self addPlane];
//强制开始第一个动画,来设置plane的位置
[self animate];
//初始化拖动手势
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:panGesture];
}
addPlane
函数是很简单明了的。它创建CAGradientLayer,并把它作为transformLayer的sublayer。
/**创建一个CAGradientLayer**/
-(void)addPlane{
CGSize planeSize = CGSizeMake(250, 150);
//初始化图层
CAGradientLayer *layer = [CAGradientLayer layer];
//设置frame和锚点
layer.frame = CGRectMake(480/2 - planeSize.width/2, 320/2 - planeSize.height/2 -20, planeSize.width, planeSize.height);
layer.anchorPoint = CGPointMake(0.5, 0.5);
//设计边框和圆角
layer.borderColor = [[UIColor colorWithWhite:1.0 alpha:0.3]CGColor];
layer.cornerRadius = 10;
layer.borderWidth = 4;
//设置渐变的颜色
layer.colors = [NSArray arrayWithObjects:
(id)[UIColor purpleColor].CGColor,
(id)[UIColor redColor].CGColor,
nil];
layer.locations = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0f],
[NSNumber numberWithFloat:1.0f],
nil];
//设置阴影
layer.shadowColor = [[UIColor blackColor]CGColor];
layer.shadowOpacity = 1;
layer.shadowRadius = 20;
//doubleSided设为yes,如果想在face被转过去的时候能被看见
layer.doubleSided = YES;
//添加到transformLayer上
[transformLayer addSublayer:layer];
}
在plane被创建后,在viewDidLoad
方法中,我们调用了animate
。
这个方法的主要功能是更新Plane的位置。在我们第一次调用animate
时,触摸事件并没有发生。
/** This function performs the transformation on each plane **/
-(void)animate{
//每个plane对应的角度
float degForPlane = 360 / [[transformLayer sublayers] count];
//当前角度的偏移量
float degX = angle;
for (CALayer *layer in [transformLayer sublayers]) {
//Create the Matrix identity
CATransform3D t = CATransform3DIdentity;
//Setup the perspective modifying the matrix elementat [3][4]
t.m34 = 1.0f / - 1000.0f;
//旋转
t = CATransform3DRotate(t, degToRad(degX), 0.0f, 1.0f, 0.0f);
//移动
t = CATransform3DTranslate(t, 0.0f, 0.0f, 250.0f);
//禁止动画
[CATransaction setAnimationDuration:0.0];
//在当前的layer上执行动画
layer.transform = t;
//Add the degree needed for the next plane
degX += degForPlane;
}
}
defForPlane变量是每一个plane要在先前的plane上旋转的角度值,来形成一个360的圈。用图片解释如下:
-(void)pan:(UIPanGestureRecognizer*)gesture{
//获取x方向上的位移
float xOffset = [gesture translationInView:self.view].x;
//在手势开始的时候,offset重置为0
if(gesture.state == UIGestureRecognizerStateBegan){
XPanOffset = 0;
}
//the distance covered since the last gesture event (I slow down a bit the final rotation multiplying by 0.5)
float movedBy = xOffset * 0.5 - XPanOffset;
//Calculate the offset from the previous gesture event
XPanOffset += movedBy;
//Add the offset to the current angle
angle += movedBy;
//Update the plane
[self animate];
}