前面的三篇文章介绍了如何使用CGAffineTransform来使UIView绘图更有效率。这个技术限制你只能进行二维变形:平移、旋转、缩放和倾斜。而在图层中,可以通过添加视角来应用三维变形。这常常被称为2.5D,因为他不能让图层真正成为三维对象(和OPENGL ES不同)。不过,它确实模拟出了三维的运动效果。
旋转图层基于一个锚点进行。默认情况下,抹点位于图层中心,一般式{0.5,0.5}。你可以将它移到图层中的任何位置,以便于围绕一条边或一个角旋转。锚点的度量以单位方形而不是点为参照。所以无论图层有多大,右下角的坐标都是{1.0,1.0};
下面是一个三维盒子的例子
BoxViewController.h
@interface BoxViewController : UIViewController {
}
@property (nonatomic,readwrite,strong) CALayer *topLayer;
@property (nonatomic,readwrite,strong) CALayer *bottomLayer;
@property (nonatomic,readwrite,strong) CALayer *leftLayer;
@property (nonatomic,readwrite,strong) CALayer *rightLayer;
@property (nonatomic,readwrite,strong) CALayer *frontLayer;
@property (nonatomic,readwrite,strong) CALayer *backLayer;
@end
BoxViewController.m
@implementation BoxViewController
const CGFloat kSize = 100;
const CGFloat kPanScale = 1./100;
-(CALayer *)layerWithColor:(UIColor *)color transform:(CATransform3D)transform
{
CALayer *layer = [CALayer layer];
layer.backgroundColor = [color CGColor];
layer.bounds = CGRectMake(0, 0, kSize, kSize);
layer.position = self.view.center;
layer.transform = transform;
[self.view.layer addSublayer:layer];
return layer;
}
static CATransform3D MakePerspetiveTransform()
{
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1./200;
return perspective;
}
-(void)viewDidLoad
{
[super viewDidLoad];
CATransform3D transform;
transform = CATransform3DMakeTranslation(0, -kSize/2,0);
transform = CATransform3DRotate(transform, M_PI_2, 1.0, 0, 0);
self.topLayer = [self layerWithColor:[UIColor redColor] transform:transform];
transform = CATransform3DMakeTranslation(0, kSize/2,0);
transform = CATransform3DRotate(transform, M_PI_2, 1.0, 0, 0);
self.bottomLayer = [self layerWithColor:[UIColor greenColor] transform:transform];
transform = CATransform3DMakeTranslation(kSize/2,0,0);
transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
self.rightLayer = [self layerWithColor:[UIColor blueColor] transform:transform];
transform = CATransform3DMakeTranslation(-kSize/2,0,0);
transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
self.rightLayer = [self layerWithColor:[UIColor cyanColor] transform:transform];
transform = CATransform3DMakeTranslation(0,0,-kSize/2);
transform = CATransform3DRotate(transform, M_PI_2, 0, 0, 0);
self.backLayer = [self layerWithColor:[UIColor yellowColor] transform:transform];
transform = CATransform3DMakeTranslation(0,0,kSize/2);
transform = CATransform3DRotate(transform, M_PI_2, 0, 0, 0);
self.frontLayer = [self layerWithColor:[UIColor magentaColor] transform:transform];
self.view.layer.sublayerTransform = MakePerspetiveTransform();
UIGestureRecognizer *g = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:g];
}
-(void)pan:(UIPanGestureRecognizer *)recognizer
{
CGPoint translation = [recognizer translationInView:self.view];
CATransform3D transform = MakePerspetiveTransform();
transform = CATransform3DRotate(transform, kPanScale * translation.x, 0, 1, 0);
transform = CATransform3DRotate(transform, -kPanScale * translation.x, 1, 0, 0);
self.view.layer.sublayerTransform = transform;
}
@end
BoxViewController 展示了如何创建一个简单的方形并基于视角旋转。所有的图层都是使用layerWithColor:transform:方法创建的。注意,所有图层拥有相同的position。他们只能把方形经过平移和旋转的变形结果显示在屏幕上。
可以使用透视的sublayerTransform(一个针对所有子图层但对当前图层无效的变形)。这里不会谈及数学,不过3D变形矩阵的m34的位置应该设置为-1/EYE_DISTANCE。大多数情况下,2000个单位就可以做的很好了,不过你可以通过“缩放摄像机”来调整。
还可以通过设置position和zPosition(而不是平移)来构造方形,如以下代码所示
-(void)viewDidLoad
{
[super viewDidLoad];
CATransformLayer *contentLayer = [CATransformLayer layer];
contentLayer.frame = self.view.layer.bounds;
CGSize size = contentLayer.bounds.size;
contentLayer.transform = CATransform3DMakeTranslation(size.width/2, size.height/2, 0);
[self.view.layer addSublayer:contentLayer];
self.contentLayer = contentLayer;
self.topLayer = [self layerAtx:0 y:-kSize/2 z:0 color:[UIColor redColor] transform:MakeSideRotation(1,0,0)];
....
}
-(CALayer *)layerAtx:(CGFloat)x y:(CGFloat)y z:(CGFloat)z color:(UIColor *)color transform:(CATransform3D)transform
{
CALayer *layer = [CALayer layer];
layer.backgroundColor = [color CGColor];
layer.bounds = CGRectMake(0, 0, kSize, kSize);
layer.position = CGPointMake(x, y);
layer.zPosition = z;
layer.transform = transform;
[self.contentLayer addSublayer:layer];
return layer;
}
-(void)pan:(UIPanGestureRecognizer *)recognizer
{
CGPoint translation = [recognizer translationInView:self.view];
CATransform3D transform = MakePerspetiveTransform();
transform = CATransform3DRotate(transform, kPanScale * translation.x, 0, 1, 0);
transform = CATransform3DRotate(transform, -kPanScale * translation.x, 1, 0, 0);
self.view.layer.sublayerTransform = transform;
}
你现在需要再其中插入CATransformLayer。如果只使用CALayer,那么zPosition只能用于计算图层显示顺序,而无法用来决定空间定位。这样就会使盒形看起来是扁平的。CATransformLayer支持zPosition并且不需要透视变形。