CoreAnimation之变换

作者: abb266389fd0 | 来源:发表于2016-12-01 14:54 被阅读367次

    CoreAnimation之CALayer基础

    1. CGAffineTransform

    • CGAffineTransform基础

    UIView有一个CGAffineTransform类型的属性,叫transform,用于在二维空间做旋转,缩放和平移。CALayer也有一个与之对应的属性叫affineTransform。CGAffineTransform所涉及的矩阵知识,这里有一篇文章说得很清楚: IOS矩阵之后的数学知识。CGPoint与CGAffineTransform的关系如图:

    Paste_Image.png
    用CGPoint的每一列和CGAffineTransform矩阵的每一行对应元素相乘再求和,就形成了一个新的CGPoint类型的结果。要解释一下图中显示的灰色元素,为了能让矩阵做乘法,左边矩阵的列数一定要和右边矩阵的行数个数相同,所以要给矩阵填充一些标志值,使得既可以让矩阵做乘法,又不改变运算结果,并且没必要存储这些添加的值,因为它们的值不会发生变化,但是要用来做运算。具体运算过程是这样的:
    Paste_Image.png
    当对图层应用变换矩阵,图层矩形内的每一个点都被相应地做变换,从而形成一个新的四边形的形状。CGAffineTransform中的“仿射”的意思是无论变换矩阵用什么值,图层中平行的两条线在变换之后任然保持平行,CGAffineTransform可以做出任意符合上述标注的变换。下图显示了一些仿射的和非仿射的变换:
    Paste_Image.png
    • CGAffineTransform应用

    CGAffineTransform的前缀CG即Core Graphics,Core Graphics提供了一系列函数,对完全没有数学基础的开发者也能够简单地做一些变换。如下几个函数都创建了一个CGAffineTransform实例:

    /* Return a transform which translates by `(tx, ty)':
         t' = [ 1 0 0 1 tx ty ] */
    CG_EXTERN CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx,
      CGFloat ty) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
    /* Return a transform which scales by `(sx, sy)':
         t' = [ sx 0 0 sy 0 0 ] */
    CG_EXTERN CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
      CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
    /* Return a transform which rotates by `angle' radians:
         t' = [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] */
    CG_EXTERN CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle)
      CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
    

    举个栗子,点击屏幕,将子视图顺时针旋转45度:

    #import "ViewController.h"
    @interface ViewController ()
    @end
    @implementation ViewController{
        UIView *layerView;
    }
    -(void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        layerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
        layerView.backgroundColor = [UIColor purpleColor];
        layerView.center = self.view.center;
        [self.view addSubview:layerView];
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [UIView animateWithDuration:1.25f animations:^{
            CGAffineTransform transform = CGAffineTransformMakeRotation(45.0f/180.0f*M_PI);
            layerView.layer.affineTransform = transform;
        }];
    }
    @end
    

    运行效果:


    running.gif

    π是弧度单位,其对应的角度为180度,Core Animation使用弧度最为参数
    弧度转角度:

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

    角度转弧度:

    #define DEGREES_TO_RADIANS(x) ((x)/180.0*M_PI)
    
    • 复合变换

    CGAffineTransformConcat方法可以混合两个已经存在的变换矩阵,在两个变换的基础上创建一个新的变换,举个栗子,修改上面的代码:

    #import "ViewController.h"
    @interface ViewController ()
    @end
    @implementation ViewController{
        UIView *layerView;
    }
    -(void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        layerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
        layerView.backgroundColor = [UIColor purpleColor];
        layerView.center = self.view.center;
        [self.view addSubview:layerView];
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [UIView animateWithDuration:1.25f animations:^{
            //CGAffineTransform transform = CGAffineTransformMakeRotation(45.0f/180.0f*M_PI);
            //layerView.layer.affineTransform = transform;
            
            CGAffineTransform rotation = CGAffineTransformMakeRotation(45.0f/180.0f*M_PI);
            CGAffineTransform scale = CGAffineTransformMakeScale(1.5f, 1.5f);
            
            CGAffineTransform transform = CGAffineTransformConcat(rotation, scale);
            layerView.layer.affineTransform = transform;
        }];
    }
    @end
    

    运行效果:


    running.gif

    CGAffineTransformConcat方法只能接受两个参数,要做两种以上的复合变换的话,需要用到下面这些方法:

    /* Translate `t' by `(tx, ty)' and return the result:
         t' = [ 1 0 0 1 tx ty ] * t */
    CG_EXTERN CGAffineTransform CGAffineTransformTranslate(CGAffineTransform t,
      CGFloat tx, CGFloat ty) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
    /* Scale `t' by `(sx, sy)' and return the result:
         t' = [ sx 0 0 sy 0 0 ] * t */
    CG_EXTERN CGAffineTransform CGAffineTransformScale(CGAffineTransform t,
      CGFloat sx, CGFloat sy) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
    /* Rotate `t' by `angle' radians and return the result:
         t' =  [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] * t */
    CG_EXTERN CGAffineTransform CGAffineTransformRotate(CGAffineTransform t,
      CGFloat angle) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
    

    举个栗子,我们来用这些函数组合一个更加复杂的变换,先缩小50%,再顺时针旋转30度,最后向右移动200个像,继续修改上面的代码:

    #import "ViewController.h"
    @interface ViewController ()
    @end
    @implementation ViewController{
        UIView *layerView;
    }
    -(void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        layerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
        layerView.backgroundColor = [UIColor purpleColor];
        layerView.center = self.view.center;
        [self.view addSubview:layerView];
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [UIView animateWithDuration:1.25f animations:^{
            //CGAffineTransform transform = CGAffineTransformMakeRotation(45.0f/180.0f*M_PI);
            //layerView.layer.affineTransform = transform;
            
            //CGAffineTransform rotation = CGAffineTransformMakeRotation(45.0f/180.0f*M_PI);
            //CGAffineTransform scale = CGAffineTransformMakeScale(1.5f, 1.5f);
            
            //CGAffineTransform transform = CGAffineTransformConcat(rotation, scale);
            //layerView.layer.affineTransform = transform;
            
            CGAffineTransform transform = CGAffineTransformIdentity;
            transform = CGAffineTransformScale(transform, 0.5f, 0.5f);
            transform = CGAffineTransformRotate(transform, 30.0f/180.0f*M_PI);
            transform = CGAffineTransformTranslate(transform, 200.0f, 0.0f);
            layerView.layer.affineTransform = transform;
        }];
    }
    @end
    

    运行效果:


    running.gif

    有些需要注意的地方:图片向右边发生了平移,但并没有指定距离那么远(200像素),另外它还有点向下发生了平移。原因在于当你按顺序做了变换,上一个变换的结果将会影响之后的变换,所以200像素的向右平移同样也被旋转了30度,缩小了50%,所以它实际上是斜向移动了100像素。这意味着变换的顺序会影响最终的结果,也就是说旋转之后的平移和平移之后的旋转结果可能不同。


    2. CATransform3D

    • CATransform3D基础

    affineTransform属性只能用来做2D变换,CALayer还有一个用来做3D变换的属性叫transform,其类型是CATransform3D类型。和CGAffineTransform类似,CATransform3D也是一个矩阵,但是和2x3的矩阵不同,CATransform3D是一个可以在3维空间内做变换的4x4的矩阵:


    Paste_Image.png

    我们对X轴和Y轴比较熟悉了,分别以右和下为正方向,Z轴和这两个轴分别垂直,指向视角外为正方向:


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

    CATransform3D类型的矩阵,和Core Graphics的函数类似,但是3D的平移和旋转多处了一个z参数,并且旋转函数除了angle之外多出了x,y,z三个参数,分别决定了每个坐标轴方向上的旋转:

    /* Returns a transform that translates by '(tx, ty, tz)':
     * t' =  [1 0 0 0; 0 1 0 0; 0 0 1 0; tx ty tz 1]. */
    CA_EXTERN CATransform3D CATransform3DMakeTranslation (CGFloat tx,
        CGFloat ty, CGFloat tz)
        CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
    /* Returns a transform that scales by `(sx, sy, sz)':
     * t' = [sx 0 0 0; 0 sy 0 0; 0 0 sz 0; 0 0 0 1]. */
    CA_EXTERN CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy,
        CGFloat sz)
        CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
    /* Returns a transform that rotates by 'angle' radians about the vector
     * '(x, y, z)'. If the vector has length zero the identity transform is
     * returned. */
    CA_EXTERN CATransform3D CATransform3DMakeRotation (CGFloat angle, CGFloat x,
        CGFloat y, CGFloat z)
        CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);
    

    举个栗子,点击屏幕将子视图旋转-45度:

    #import "ViewController.h"
    @interface ViewController ()
    @end
    @implementation ViewController{
        UIView *layerView;
    }
    -(void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        layerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150.0f, 150.0f)];
        layerView.center = self.view.center;
        layerView.layer.backgroundColor = [UIColor blueColor].CGColor;
        [self.view addSubview:layerView];
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [UIView animateWithDuration:1.25f animations:^{
            CATransform3D transform = CATransform3DIdentity;
            transform = CATransform3DRotate(transform, -(45.0f/180.0f*M_PI), 0.0f, 1.0f, 0.0f);
            layerView.layer.transform = transform;
        }];
    }
    @end
    

    运行效果:


    running.gif

    看起来好像只是宽度被压扁了而已,这是因为我们在用一个斜向的视角看它,而不是透视。

    • 透视效果

    CATransform3D的透视效果通过一个矩阵中一个很简单的元素来控制:m34。m34用于按比例缩放X和Y的值来计算到底要离视角多远,其默认值是0,我们可以通过设置m34为-1.0 / d来应用透视效果,d代表了想象中视角相机和屏幕之间的距离,以像素为单位,那应该如何计算这个距离呢?实际上并不需要如何计算,大概估算一个就好了,一般是500-1000。修改上面的代码:

    #import "ViewController.h"
    @interface ViewController ()
    @end
    @implementation ViewController{
        UIView *layerView;
    }
    -(void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        layerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150.0f, 150.0f)];
        layerView.center = self.view.center;
        layerView.layer.backgroundColor = [UIColor blueColor].CGColor;
        [self.view addSubview:layerView];
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [UIView animateWithDuration:1.25f animations:^{
            CATransform3D transform = CATransform3DIdentity;
            transform.m34 = -1.0f / 500.0f;
            transform = CATransform3DRotate(transform, -(45.0f/180.0f*M_PI), 0.0f, 1.0f, 0.0f);
            layerView.layer.transform = transform;
        }];
    }
    @end
    

    运行效果:


    running.gif
    • 灭点

    当在透视角度绘图的时候,远离相机视角的物体将会变小变远,当远离到一个极限距离,它们可能就缩成了一个点,于是所有的物体最后都汇聚消失在同一个点,这个点叫灭点。

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

    • sublayerTransform

    如果有多个子视图或者子图层,每个都做3D变换,那就需要分别设置相同的m34值,并且确保在变换之前都在屏幕中央共享同一个position,这样操作起来很麻烦,CALayer有一个叫做sublayerTransform的属性可以解决这个问题。

    sublayerTransform也是CATransform3D类型,但和对一个图层的变换不同,它影响到所有的子图层,这意味着你可以一次性对包含这些图层的容器做变换。

    使用sublayerTransform,灭点被设置在容器图层的中点,从而不需要再对子图层分别设置了。这意味着你可以随意使用position和frame来放置子图层,而不需要把它们放置在屏幕中点,然后为了保证统一的灭点用变换来做平移。

    • doubleSided

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

    #import "ViewController.h"
    @interface ViewController ()
    @end
    @implementation ViewController{
        UIView *topView;
        UIView *bottomView;
    }
    -(void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        UIImage *image = [UIImage imageNamed:@"3"];
        
        topView = [[UIView alloc] initWithFrame:CGRectMake(85.0f, 100.0f, 150.0f, 150.0f)];
        bottomView = [[UIView alloc] initWithFrame:CGRectMake(85.0f, 300.0f, 150.0f, 150.0f)];
        
        bottomView.layer.doubleSided = NO;
        
        [self configView:topView image:image];
        [self configView:bottomView image:image];
    }
    -(void)configView:(UIView *)aView image:(UIImage *)aImage{
        aView.layer.backgroundColor = [UIColor greenColor].CGColor;
        aView.layer.contents = (__bridge id _Nullable)(aImage.CGImage);
        aView.layer.contentsGravity = kCAGravityResizeAspect;
        aView.layer.contentsScale = [UIScreen mainScreen].scale;
        [self.view addSubview:aView];
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [UIView animateWithDuration:5.0f animations:^{
            CATransform3D transform = CATransform3DIdentity;
            transform = CATransform3DRotate(transform, M_PI, 0.0f, 1.0f, 0.0f);
            topView.layer.transform = transform;
            bottomView.layer.transform = transform;
        }];
    }
    @end
    

    运行效果:


    running.gif

    3. 固体对象

    现在我们懂得了在3D空间的一些图层布局的基础,接下来试着创建一个固态的3D对象(实际上是一个技术上所谓的空洞对象,但它以固态呈现),我们用三个独立的视图来构建一个残缺的正方体

    Interface Build布局如图:

    Paste_Image.png

    代码:

    #import "TestViewController.h"
    @interface TestViewController ()
    @property (nonatomic, weak) IBOutlet UIView *containerView;
    @property (nonatomic, strong) IBOutletCollection(UIView) NSArray *faces;
    @end
    
    @implementation TestViewController
    
    - (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform{
        //get the face view and add it to the container
        UIView *face = self.faces[index];
        [self.containerView addSubview:face];
        //center the face view within the container
        CGSize containerSize = self.containerView.bounds.size;
        face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
        // apply the transform
        face.layer.transform = transform;
    }
    
    - (void)viewDidLoad{
        [super viewDidLoad];
        //set up the container sublayer transform
        CATransform3D perspective = CATransform3DIdentity;
        perspective.m34 = -1.0 / 500.0;
        self.containerView.layer.sublayerTransform = perspective;
        //add cube face 1
        CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);
        [self addFace:0 withTransform:transform];
        //add cube face 2
        transform = CATransform3DMakeTranslation(100, 0, 0);
        transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
        [self addFace:1 withTransform:transform];
        //add cube face 3
        transform = CATransform3DMakeTranslation(0, -100, 0);
        transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
        [self addFace:2 withTransform:transform];
    }
    @end
    

    运行效果:

    Paste_Image.png

    从这个角度看正方体并不是很明显,看起来只是一个方块,为了更好地欣赏它,我们将更换一个不同的视角

    但是旋转这个正方体将会显得很笨重,因为我们要单独对每个面做旋转,这时候我们之前说的sublayerTransform就派上了用场

    添加两行代码去旋转containerView图层的perspective变换矩阵:

        perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);
        perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);
    

    把相机(或者相对相机的整个场景)绕Y轴旋转45度,并且绕X轴旋转45度,现在从另一个角度去观察正方体,就能看出它的真实面貌:

    Paste_Image.png

    效果出来了,但是我还想旋转一下这个正方体,看看其他的面,怎么办呢

    so easy!绕y轴旋转咯

    说干就干,修改代码:

    #import "TestViewController.h"
    @interface TestViewController ()
    @property (nonatomic, weak) IBOutlet UIView *containerView;
    @property (nonatomic, strong) IBOutletCollection(UIView) NSArray *faces;
    @end
    
    @implementation TestViewController{
        CATransform3D rotateTransform;
    }
    
    - (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform{
        //get the face view and add it to the container
        UIView *face = self.faces[index];
        [self.containerView addSubview:face];
        //center the face view within the container
        CGSize containerSize = self.containerView.bounds.size;
        face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
        // apply the transform
        face.layer.transform = transform;
    }
    
    - (void)viewDidLoad{
        [super viewDidLoad];
        //set up the container sublayer transform
        
        rotateTransform = CATransform3DIdentity;
        rotateTransform.m34 = -1.0f / 500.0f;
        
        CATransform3D perspective = CATransform3DIdentity;
        perspective.m34 = -1.0 / 500.0;
        perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);
        perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);
        self.containerView.layer.sublayerTransform = perspective;
    
        //add cube face 1
        CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);
        [self addFace:0 withTransform:transform];
        //add cube face 2
        transform = CATransform3DMakeTranslation(100, 0, 0);
        transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
        [self addFace:1 withTransform:transform];
        //add cube face 3
        transform = CATransform3DMakeTranslation(0, -100, 0);
        transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
        [self addFace:2 withTransform:transform];
    }
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [NSTimer scheduledTimerWithTimeInterval:0.01f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
    }
    
    -(void)timerAction{
        static CGFloat angle = 1.0f;
        rotateTransform = CATransform3DRotate(rotateTransform, angle/180.0f*M_PI, 0.0f, 1.0f, 0.0f);
        self.containerView.layer.transform = rotateTransform;
    }
    
    @end
    

    跑起来:

    running.gif

    纳尼,这是什么鬼👻

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

    总之一句话,图层是扁平的,直接把superLayer绕y轴旋转的方法行不通

    要想实现旋转正方体的效果,就得将所有的子图层全部挨个儿做变换

    到最后还得用sublayerTransform

    继续修改代码:

    #import "TestViewController.h"
    @interface TestViewController ()
    @property (nonatomic, weak) IBOutlet UIView *containerView;
    @property (nonatomic, strong) IBOutletCollection(UIView) NSArray *faces;
    @end
    
    @implementation TestViewController{
    //    CATransform3D rotateTransform;
    }
    
    - (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform{
        //get the face view and add it to the container
        UIView *face = self.faces[index];
        [self.containerView addSubview:face];
        //center the face view within the container
        CGSize containerSize = self.containerView.bounds.size;
        face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
        // apply the transform
        face.layer.transform = transform;
    }
    
    - (void)viewDidLoad{
        [super viewDidLoad];
        //set up the container sublayer transform
        
    //    rotateTransform = CATransform3DIdentity;
    //    rotateTransform.m34 = -1.0f / 500.0f;
        
        CATransform3D perspective = CATransform3DIdentity;
        perspective.m34 = -1.0 / 500.0;
        perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);
        perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);
        self.containerView.layer.sublayerTransform = perspective;
        
        //add cube face 1
        CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);
        [self addFace:0 withTransform:transform];
        //add cube face 2
        transform = CATransform3DMakeTranslation(100, 0, 0);
        transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
        [self addFace:1 withTransform:transform];
        //add cube face 3
        transform = CATransform3DMakeTranslation(0, -100, 0);
        transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
        [self addFace:2 withTransform:transform];
    }
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        [NSTimer scheduledTimerWithTimeInterval:0.01f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
    }
    
    -(void)timerAction{
        static CGFloat angle = 1.0f;
        CATransform3D transform3d = self.containerView.layer.sublayerTransform;
        transform3d = CATransform3DRotate(transform3d, angle/180.0f*M_PI, 0.0f, 1.0f, 0.0f);
        self.containerView.layer.sublayerTransform = transform3d;
        
    //    rotateTransform = CATransform3DRotate(rotateTransform, angle/180.0f*M_PI, 0.0f, 1.0f, 0.0f);
    //    self.containerView.layer.transform = rotateTransform;
    }
    
    @end
    

    最终运行效果:

    running.gif

    相关文章

      网友评论

      本文标题:CoreAnimation之变换

      本文链接:https://www.haomeiwen.com/subject/wgltmttx.html