iOS Core Animation Advanced Techniques学习笔记(2)

变换

仿射变换

CGAffineTransform是一个可以和二维空间向量(如CGPoint)做乘法的3*2的矩阵。当对图层应用变换矩阵,图层内的每一个点都被相应地做变换,从而形成一个新的四边形的形状。CGAffineTransform中仿射的意思是无论变换矩阵用什么值,图层中平行的两条线在变换后任然保持平行。

UIView可以通过设置transform属性做变换,但实际上它只是封装了内部图层的变换。CALayer同样也有一个transform属性,但它的类型是CATransform3D,而不是CGAffineTransform。CALayer对应于UIView的transform属性叫做affineTransform

CGAffineTransformMakeRotation(CGFloat angle)              // 旋转
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)        // 缩放
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)  // 平移

由于iOS变换函数使用弧度而不是角度作为单位,所以做旋转变换的时候可以使用如下宏来将角度换算成弧度:

#define RADIANS_TO_DEGREES(x) ((x)/M_PI*180.0)

混合变换

混合变换函数:

CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2)

当生成一个混合变换的时候,首先需要创建一个CGAffineTransform类型的空值,矩阵论中称为单位矩阵,Core Graphics 中提供了一个方便的常量:

CGAffineTransformIdentity

如果需要混合两个已经存在的变换矩阵,就可以使用如下方法,在两个变换的基础上创建一个新的变换:

CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);

示例代码:

- (void)viewDidLoad
{
    [super viewDidLoad]; 
    
    //create a new transform
    CGAffineTransform transform = CGAffineTransformIdentity; 
    
    //scale by 50%
    transform = CGAffineTransformScale(transform, 0.5, 0.5); 
    
    //rotate by 30 degrees
    transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0); 
    
    //translate by 200 points
    transform = CGAffineTransformTranslate(transform, 200, 0);
    
    //apply transform to layer
    self.layerView.layer.affineTransform = transform;
}

3D变换

CATransform3D是一个可以在3维空间内做变换的4*4的矩阵。和CGAffineTransform矩阵类似,Core Animation提供了一系列的方法用来创建和组合CATransform3D类型的矩阵,和Core Graphics的函数类似,但是3D的平移和旋转多处了一个z参数,并且旋转函数除了angle之外多出了x,y,z三个参数,分别决定了每个坐标轴方向上的旋转:

CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz) 
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)

绕Z轴的旋转等同于之前二维空间的仿射旋转,但是绕X轴和Y轴的旋转就突破了屏幕的二维空间,并且在用户视角看来发生了倾斜。

如果要实现透视效果,还需要引入投影变换(又称作z变换)来对除了旋转之外的变换矩阵做一些修改,而这可以通过修改矩阵值来实现。CATransform3D中的透视效果通过矩阵中一个很简单的元素来控制:m34m34用于按比例缩放x和y的值来计算到底要离视角多远。

m34的默认值是0,可以通过设置m34为-1.0/d来应用透视效果。d代表了想象中视角相机和屏幕之间的距离,以像素为单位,通常设置为500-1000。

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //create a new transform
    CATransform3D transform = CATransform3DIdentity;
    
    //apply perspective
    transform.m34 = - 1.0 / 500.0;
    
    //rotate by 45 degrees along the Y axis
    transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
    
    //apply to layer
    self.layerView.layer.transform = transform;
}

灭点

灭点是指在透视角度物体远离视角的那端汇聚消失的那个点。在现实中,这个点通常是视图的中心,于是为了在屏幕中创建拟真效果的透视,这个点应该聚在屏幕中点,或者至少是包含所有3D对象的视图中点。

Core Animation定义了这个点位于变换图层的anchorPoint。这就是说,当图层发生变换时,这个点永远位于图层变换之前anchorPoint的位置。

当改变一个图层的position,也就改变了它的灭点,做3D变换的时候要时刻记住这一点,当视图通过调整m34来让它更加有3D效果,应该首先把它放置于屏幕中央,然后通过平移来把它移动到指定位置,而不是直接改变它的position,这样所有的3D图层都共享一个灭点。

sublayerTransform属性

如果要为多个视图或图层做3D变换并且保证灭点设置在容器图层中心,可以使用CALayer的sublayerTransform属性:

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *containerView;
@property (nonatomic, weak) IBOutlet UIView *layerView1;
@property (nonatomic, weak) IBOutlet UIView *layerView2;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //apply perspective transform to container
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = - 1.0 / 500.0;
    self.containerView.layer.sublayerTransform = perspective;
    
    //rotate layerView1 by 45 degrees along the Y axis
    CATransform3D transform1 = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
    self.layerView1.layer.transform = transform1;
    
    //rotate layerView2 by 45 degrees along the Y axis
    CATransform3D transform2 = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);
    self.layerView2.layer.transform = transform2;
}

禁用背面绘制:

layer.doubleSided = NO;

固体对象

示例代码:

@implementation RootViewController

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0 / 500;
    perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);
    perspective = CATransform3DRotate(perspective, -M_PI, 0, 1, 0);
    
    self.containerView.layer.sublayerTransform = perspective;
    
    CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);
    [self addFace:0 withTransform:transform];
    
    transform = CATransform3DMakeTranslation(100, 0, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
    [self addFace:1 withTransform:transform];
    
    transform = CATransform3DMakeTranslation(-100, 0, 0);
    transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);
    [self addFace:2 withTransform:transform];
    
    transform = CATransform3DMakeTranslation(0, 100, 0);
    transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);
    [self addFace:3 withTransform:transform];
    
    transform = CATransform3DMakeTranslation(0, -100, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
    [self addFace:4 withTransform:transform];
    
    transform = CATransform3DMakeTranslation(0, 0, -100);
    transform = CATransform3DRotate(transform, M_PI, 0, 1, 0);
    [self addFace:5 withTransform:transform];
}

- (void)addFace:(NSInteger)index withTransform:(CATransform3D)transfrom {

    UIView *view = self.faces[index];
    
    [self.containerView addSubview:view];
    
    view.center = self.containerView.center;
    
    view.layer.transform = transfrom;
    view.layer.borderWidth = 1.0f;
}

你可能感兴趣的:(iOS Core Animation Advanced Techniques学习笔记(2))