UIView的transform
属性是CGAffineTransform类型,用于在二维空间做旋转,缩放和平移。
CGAffineTransform是一个可以和二维空间向量做乘法的3 * 2矩阵。如下图:
仿射变换
当对图层应用变换矩阵,图层矩形内的每一个点都被相应的做变换,从而形成一个新的形状。CGAffineTransform
仿射变换的仿射
意思是无论变换矩阵用什么值,图层中平行的两条线在变换之后仍然保持平行。
看了上边的一些概念,可能觉得雾里云里,不如我们直接上代码和效果。
CGAffineTransform是CG开头的,也就是说是在Core Graphics框架里的,这个框架为我们提供了好用的函数可以让我们不必自己去亲自计算矩阵的相乘。
//常用的三个函数:
CGAffineTransformMakeRotation(CGFloat angle)
// 旋转 表示旋转多少角度,如果是正数的,会按照顺时针旋转,如果负数,则逆时针旋转。
//注意,这个函数传入的参数不是我们平常说的角度而是弧度。如果觉得分不清楚,或总是混淆的话,可以用下边给出的两个宏做计算。
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
// 缩放 两个参数分别表示水平方向(x轴)和竖直方向(y轴)的缩放比例。
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
// 平移 两个参数分别表示水平方向(x轴)和竖直方向(y轴)上平移的位移(以点`point`计)
//弧度转角度
#define RADIANS_TO_DEGREES(x) ((x)/M_PI*180.0)//传入弧度 eg: M_PI_4
//角度转弧度
#define DEGREES_TO_RADIANS(x) ((x)/180.0*M_PI)//传入度数eg:45度
- (void)test0
{
///右转90度
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_2);
//以下两个赋值效果一样。最终都是作用于layer上。
self.label.transform = transform;
// self.label.layer.affineTransform = transform;
}
- (void)test1
{
//水平方向缩小0.5倍,竖直方向缩小0.8倍
CGAffineTransform transform = CGAffineTransformMakeScale(0.5, 0.8);
self.label.layer.affineTransform = transform;
}
- (void)test2
{
//水平方向上平移20;竖直方向平移10
CGAffineTransform transform = CGAffineTransformMakeTranslation(20, 10);
self.label.layer.affineTransform = transform;
}
图中蓝色视图是红色视图的父视图,作为初始状态和形变后的样子做对比。第一张是选择,第二种是缩放,第三张是平移。
混合变换
如果想让一个view既缩放又旋转,就用到混合变换了。Core Graphics也给我们提供了方便好用的接口。
/*
CGAffineTransformIdentity: //单位矩阵
“CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)”
//两个变换的基础上生成另一个变换
“CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);”
*/
@property(nonatomic) CGAffineTransform transform; // default is CGAffineTransformIdentity. animatable
//UIView的transform的属性的默认值就是CGAffineTransformIdentity。
调用这些函数要传入一个初始值,即CGAffineTransformIdentity
,这个是CGAffineTransform类型的空值。也是UIView的transform属性的默认值。以上几个函数,可以将不同的效果叠加。
- (void)mixTest0
{
CGAffineTransform transfrom = CGAffineTransformIdentity;
transfrom = CGAffineTransformRotate(transfrom,DEGREES_TO_RADIANS(60));//旋转60度
transfrom = CGAffineTransformScale(transfrom, 0.5, 0.6);
transfrom = CGAffineTransformTranslate(transfrom, 0, 50);
//没有垂直向下移动50,因为会叠加上两个形变的结果,相当于向斜下方移动了50 * 0.6
self.label.layer.affineTransform = transfrom;
//“后边的形变会作用在之前形变的结果之上,也就是说旋转之后的平移和平移之后的旋转结果可能不同。
}
-
3D变换
上边说的仿射变换和混合变换都是在平面上的效果,图层layer的transform
属性(CATransform3D类型 )可以让图层在3D空间内移动或者旋转。CATransform3D
是在3维空间内做变换的4 * 4的矩阵。
//3D的变换比仿射变换多了一个角度和一个z轴的参数。
//CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
//CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
//CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)”
下图是3D空间内,针对x,y,z轴以及其旋转方向。
- (void)test3D_x
{
//在竖直方向压缩了 实际是沿着竖直方向倾斜了45度去看 如果是90度则完全看不到
CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 1, 0, 0);
self.label.layer.transform = transform;
}
- (void)test3D_y
{
//效果是在水平方向压缩了 实际是沿着水平方向倾斜了45度去看 如果是90度则完全看不到
CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
self.label.layer.transform = transform;
}
- (void)test3D_z//效果是在正对着用户的方向上旋转了45度
{
CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
self.label.layer.transform = transform;
}
其实单直接对以上函数的参数做变动,看不出太多效果,和自己想象中的3D效果不一样。看着好像是图层进行了压缩,其实是因为相当于我们换个角度去看图层导致。
要想达到透视效果,我们需要对
CATransform3D
这个矩阵做操作。
-
透视
我们不需要去管矩阵计算怎么复杂,我们只要控制一个矩阵中m34
的值就可以。m34
用于按比例缩放X和Y的值来计算到底要离视角有多远。所以我们可以用CATransform3D
中m34
的值用来做透视。
m34
的值默认是0,可以通过设置m34
为-1.0 / d
来实现透视效果,d的取值范围500~1000
。
- (void)testPerspective_x
{
CATransform3D transform = CATransform3DIdentity;
transform.m34 = - 1.0 / 500;
transform = CATransform3DRotate(transform, M_PI_4, 1, 0, 0);
self.label.layer.transform = transform;
}
- (void)testPerspective_y
{
CATransform3D transform = CATransform3DIdentity;
transform.m34 = - 1.0 / 500;
transform = CATransform3DRotate(transform, M_PI_4, 0, 1, 0);
self.label.layer.transform = transform;
}
- (void)testPerspective_z
{
CATransform3D transform = CATransform3DIdentity;
transform.m34 = - 1.0 / 500;
transform = CATransform3DRotate(transform, M_PI_4, 0, 0, 1);
self.label.layer.transform = transform;
}
结果如上图,我们设置了
m34
的值后,看着才有了3D的效果。具体你效果需要什么样,可以根据自己的需要进行设置。总结:
变换这块自己平常不怎么用到,偶尔需要用到的时候也总想不起来,在这里总结一下,以后便于查找学习。希望对看到的读者也有所帮助。感谢下边书籍的翻译者,感谢。配套Demo可供参考。
参考书籍:ios核心动画高级技巧