iOS核心动画笔记5-变换

﹥>﹥吖頭↗ 提交于 2019-11-30 12:34:14

变换

这一节主要讲的是, 对图层进行旋转,缩放扭曲等操作.

1. 仿射变换

在视图层面上, UIView有个属性叫做 transform, 可以进行二维层面上的图层变换. 主要包括: 旋转/平移/缩放操作.

当图层应用变换矩阵进行变换时候, 图层矩形内的每个点都会被相应的进行变化, 从而形成一个新的四边形的形状. 仿射变换中"仿射"的意思是无论变换矩阵用什么值, 图层中平行的两条线在变换之后仍然保持平行.

2. 创建仿射变换CGAffineTransform

CGAffineTransform实际上是一个3*2的矩阵, 在OC中用结构体表示, (CG前缀表示属于Core Graphcs框架), CG框架提供了方便的方法来创建CGAffineTransform结构体, 如下三个是创建旋转, 缩放,平移的方法, 返回值是CGAffineTransform类型结构体.

CGAffineTransformMakeRotation(CGFloat angle)  CGAffineTransformMakeScale(CGFloat sx, CGFloat sy) CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty) 

UIView用来做变换的属性叫做transform, 对应CALayer的属性是affineTransform. CALayer也有一个transform属性, 但是是CATransform3D类型的, 是用来做3D变换的属性.

弧度和角度的转换

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

3. 混合变换

通过CG框架我们可以简单的创建不同的仿射变换, 如果我们需要缩放的同时又要旋转, 这时候就用到了混合变换, CG框架也提供了丰富的框架, 很简单, 不解释:

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 空值状态 :CGAffineTransformIdentity.

当使用混合变换时候需要注意, 混合变换的顺序会影响结果, 也就是说, 旋转之后平移和平移之后旋转的结果很可能不一样. 原因是上一个变换的变换结果会直接影响之后的变换. 比如先缩放0.5再平移200, 实际上只会平移100, 原因是缩放时候, 平移也会被缩放掉.

4. 剪切变换

Core Graphics提供了计算变换矩阵的一些方法, 一般我们不直接使用CGAffineTransform的值, 但是如果需要创建斜切的变换, Core Graphics并没有提供相应的方法, 这就需要我们自己直接修改结构体相应的值了.

@implementation ViewController  CGAffineTransform CGAffineTransformMakeShear(CGFloat x, CGFloat y) {     CGAffineTransform transform = CGAffineTransformIdentity;     transform.c = -x;     transform.b = y;     return transform; }  - (void)viewDidLoad {     [super viewDidLoad];     //shear the layer at a 45-degree angle     self.layerView.layer.affineTransform = CGAffineTransformMakeShear(1, 0); }  @end 

5. 3D变换

CALayer的transform属性(CATransform3D类型)可以让图层在3D空间内移动或者旋转. 和CGAffineTransform类似,CATransform3D也是一个矩阵,CATransform3D是一个可以在3维空间内做变换的4x4的矩阵.

Core Animation提供一系列方法创建CATransform3D类型矩阵. 如下所示, 当然, 也有相应的混合变换的方法.

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

使用demo:

    //2.1 旋转 //    CATransform3D transform3d = CATransform3DMakeRotation(M_PI_4, 1, 0, 0);          //2.2 缩放  但是z轴上的缩放是什么鬼 ? //    CATransform3D transform3d = CATransform3DMakeScale(2, 1, 1);          //2.3 平移 //    CATransform3D transform3d = CATransform3DMakeTranslation(100, 100, 0);          //2.4 透视投影  测试证明,必须先设置m34的值, 然后再设置旋转角度, 这样才会有透视投影的效果     //create a new transform     CATransform3D transform3d = CATransform3DIdentity;     //apply perspective     transform3d.m34 = - 1.0 / 500.0;     //rotate by 45 degrees along the Y axis     transform3d = CATransform3DRotate(transform3d, M_PI_4, 0, 1, 0); 

6. 透视投影

需要我们手动的改变m34值, 默认值是0, 一般我们通过设置m34为 -1/d 来应用透视效果(d一般取值500~1000之间),

demo:

@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; }  @end 

测试发现必须先设置m34值, 然后再进行变换, 否则无效, 暂时还不知道原因.

效果:

7. 灭点

当在透视角度绘图时候, 原理相机的屋里将会越变越小, 当远到一定的距离, 就缩小成一个点了, 于是所有的物体都汇集消失在一个点, 这个点被称为 灭点. Core Animation定义这个点anchorPoint. 为了让所有图层更有3D效果, 我们可以将图层都放在同一个位置, 也就是 amchorPoint是相同的, 然后通过变换将它移动到指定位置(而不是直接更改Position), 这样所有的3D图层就都共享一个灭点了.

8. sublayerTransform属性

CALayer有个属性交sublayerTransform, 它是CATransform3D类型, 它能够影响到它所有的直接子图层. 就是说可以一次性对这些图层的容器做变换, 然后所有的子图层都集成这个变换方法.

通过在一个地方设置透视变换会变得很方便, 同时也会带来另一个好处: 灭点被设置在容器图层的中点, 从而不需要对子图层分别设置了. 意味着, 你可以随意使用position和frame来放置子图层, 而不需要把它们放置在屏幕中央, 然后为了保证统一的灭点用变换来平移.

demo:

#pragma mark - 测试通过 sublayerTransform 属性设置统一的灭点 - (void)testSublayerTransform {          UIView *bgview = [[UIView alloc] initWithFrame:CGRectMake(20, 100, 250, 200)];     [self.view addSubview:bgview];     bgview.backgroundColor = [UIColor lightGrayColor];          CATransform3D transform3D = CATransform3DIdentity;     transform3D.m34 = -1 / 500.0;          bgview.layer.sublayerTransform = transform3D;               CALayer *leftLayer = [CALayer layer];     leftLayer.backgroundColor = [UIColor redColor].CGColor;     [bgview.layer addSublayer:leftLayer];     leftLayer.frame = CGRectMake(10, 10, 80, 100);     leftLayer.transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);          CALayer *rightLayer = [CALayer layer];     rightLayer.backgroundColor = [UIColor redColor].CGColor;     [bgview.layer addSublayer:rightLayer];     rightLayer.frame = CGRectMake(CGRectGetWidth(bgview.frame)-90, 10, 80, 100);     rightLayer.transform = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);      } 

9. 背面

通过3D旋转, 我们可以将图形旋转到背面, 进而去观察图层的背面. 旋转180°时候, 会发现是一个正面的镜像图片, 就是说图层是双面绘制的. 但是, 既然背面永远不会被看到, 为什么要去绘制呢???

CALayer有个叫doubleSided的属性来控制图层的背面是否要被绘制. 这是一个BOOL类型, 默认是YES, 如果设置为NO, 那么当图层正面从相机视角消失的时候, 它将不会被绘制.

10. 扁平化图层

内图层向右旋转45°, 外图层向左旋转45°效果:

透视效果+内图层绕y轴旋转, 外图层也绕y轴旋转, 效果如下:

因为, 尽管Core Animation图层存在于3D空间之内, 但是它们不属于同一个3D空间. 每个图层的3D场景其实是扁平化的, 当你从正面观察一个图层, 看到的实际上是由子图层创建的想象出来的3D场景, 但当你倾斜时候回发现, 这个3D场景紧紧是被绘制在图层的表面了.

这个特性使Core Animation创建复杂3D场景变得十分困难, 你不能使用图层🌲创建一个3D结构的层级关系, 在相同的场景下的任何3D表面必须和同样的图层保持一致, 这是因为每个父视图都把它的子视图扁平化了. CALayer有个叫 CATransformLayer 的子类用来解决这个问题, 详见后文.

11. 固体对象

测试demo:

#pragma mark - 测试固体 - (void)testSolid {          //     CATransform3D perspective = CATransform3DIdentity;     perspective.m34 = -1.0 / 500.0;     perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 1, 0);     self.contenterView.layer.sublayerTransform = perspective;          CATransform3D transform;          // 1     UIView *first = self.faces[0];     transform = CATransform3DMakeTranslation(0, 0, 100);     first.layer.transform = transform;          //2     UIView *second = self.faces[1];     transform = CATransform3DMakeTranslation(100, 0, 0);     transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);     second.layer.transform = transform;          //3     UIView *third = self.faces[2];     transform = CATransform3DMakeTranslation(-100, 0, 0);     transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);     third.layer.transform = transform;          //4     UIView *forth = self.faces[3];     transform = CATransform3DMakeTranslation(0, 100, 0);     transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);     forth.layer.transform = transform;          //5     UIView *fifth = self.faces[4];     transform = CATransform3DMakeTranslation(0, -100, 0);     transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);     fifth.layer.transform = transform;          //6     UIView *sixth = self.faces[5];     transform = CATransform3DMakeTranslation(0, 0, -100);     sixth.layer.transform = transform;           }  - (UIView *)contenterView {          if (!_contenterView) {         _contenterView = [[UIView alloc] initWithFrame:self.view.bounds];         [self.view addSubview:_contenterView];         _contenterView.backgroundColor = [UIColor lightGrayColor];     }     return _contenterView; }  - (NSArray *)faces {          if (!_faces) {                  NSMutableArray *faces = [NSMutableArray array];         NSArray *colors = @[[UIColor redColor], [UIColor greenColor], [UIColor purpleColor], [UIColor orangeColor], [UIColor blueColor], [UIColor cyanColor]];                  for (int i = 0; i < 6; i++) {                          UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];             [self.contenterView addSubview:view];             view.backgroundColor = colors[i];             view.center = self.contenterView.center;                          [faces addObject:view];                      }         _faces = faces;     }     return _faces; } 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!