CoreAnimation之變換

CoreAnimation之CALayer基礎(chǔ)

1. CGAffineTransform

  • CGAffineTransform基礎(chǔ)

UIView有一個(gè)CGAffineTransform類型的屬性走孽,叫transform抹恳,用于在二維空間做旋轉(zhuǎn)哺眯,縮放和平移。CALayer也有一個(gè)與之對(duì)應(yīng)的屬性叫affineTransform。CGAffineTransform所涉及的矩陣知識(shí)蕴潦,這里有一篇文章說(shuō)得很清楚: IOS矩陣之后的數(shù)學(xué)知識(shí)。CGPoint與CGAffineTransform的關(guān)系如圖:

Paste_Image.png

用CGPoint的每一列和CGAffineTransform矩陣的每一行對(duì)應(yīng)元素相乘再求和,就形成了一個(gè)新的CGPoint類型的結(jié)果惋戏。要解釋一下圖中顯示的灰色元素,為了能讓矩陣做乘法他膳,左邊矩陣的列數(shù)一定要和右邊矩陣的行數(shù)個(gè)數(shù)相同响逢,所以要給矩陣填充一些標(biāo)志值,使得既可以讓矩陣做乘法棕孙,又不改變運(yùn)算結(jié)果舔亭,并且沒(méi)必要存儲(chǔ)這些添加的值,因?yàn)樗鼈兊闹挡粫?huì)發(fā)生變化蟀俊,但是要用來(lái)做運(yùn)算钦铺。具體運(yùn)算過(guò)程是這樣的:
Paste_Image.png

當(dāng)對(duì)圖層應(yīng)用變換矩陣,圖層矩形內(nèi)的每一個(gè)點(diǎn)都被相應(yīng)地做變換肢预,從而形成一個(gè)新的四邊形的形狀矛洞。CGAffineTransform中的“仿射”的意思是無(wú)論變換矩陣用什么值,圖層中平行的兩條線在變換之后任然保持平行烫映,CGAffineTransform可以做出任意符合上述標(biāo)注的變換沼本。下圖顯示了一些仿射的和非仿射的變換:
Paste_Image.png

  • CGAffineTransform應(yīng)用

CGAffineTransform的前綴CG即Core Graphics,Core Graphics提供了一系列函數(shù)锭沟,對(duì)完全沒(méi)有數(shù)學(xué)基礎(chǔ)的開發(fā)者也能夠簡(jiǎn)單地做一些變換擅威。如下幾個(gè)函數(shù)都創(chuàng)建了一個(gè)CGAffineTransform實(shí)例:

/* 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);

舉個(gè)栗子,點(diǎn)擊屏幕冈钦,將子視圖順時(shí)針旋轉(zhuǎn)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

運(yùn)行效果:


running.gif

π是弧度單位郊丛,其對(duì)應(yīng)的角度為180度李请,Core Animation使用弧度最為參數(shù)
弧度轉(zhuǎn)角度:

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

角度轉(zhuǎn)弧度:

#define DEGREES_TO_RADIANS(x) ((x)/180.0*M_PI)
  • 復(fù)合變換

CGAffineTransformConcat方法可以混合兩個(gè)已經(jīng)存在的變換矩陣,在兩個(gè)變換的基礎(chǔ)上創(chuàng)建一個(gè)新的變換厉熟,舉個(gè)栗子导盅,修改上面的代碼:

#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

運(yùn)行效果:


running.gif

CGAffineTransformConcat方法只能接受兩個(gè)參數(shù),要做兩種以上的復(fù)合變換的話揍瑟,需要用到下面這些方法:

/* 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);

舉個(gè)栗子白翻,我們來(lái)用這些函數(shù)組合一個(gè)更加復(fù)雜的變換,先縮小50%绢片,再順時(shí)針旋轉(zhuǎn)30度滤馍,最后向右移動(dòng)200個(gè)像,繼續(xù)修改上面的代碼:

#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

運(yùn)行效果:


running.gif

有些需要注意的地方:圖片向右邊發(fā)生了平移底循,但并沒(méi)有指定距離那么遠(yuǎn)(200像素)巢株,另外它還有點(diǎn)向下發(fā)生了平移。原因在于當(dāng)你按順序做了變換熙涤,上一個(gè)變換的結(jié)果將會(huì)影響之后的變換阁苞,所以200像素的向右平移同樣也被旋轉(zhuǎn)了30度,縮小了50%祠挫,所以它實(shí)際上是斜向移動(dòng)了100像素那槽。這意味著變換的順序會(huì)影響最終的結(jié)果,也就是說(shuō)旋轉(zhuǎn)之后的平移和平移之后的旋轉(zhuǎn)結(jié)果可能不同等舔。


2. CATransform3D

  • CATransform3D基礎(chǔ)

affineTransform屬性只能用來(lái)做2D變換骚灸,CALayer還有一個(gè)用來(lái)做3D變換的屬性叫transform,其類型是CATransform3D類型慌植。和CGAffineTransform類似甚牲,CATransform3D也是一個(gè)矩陣,但是和2x3的矩陣不同涤浇,CATransform3D是一個(gè)可以在3維空間內(nèi)做變換的4x4的矩陣:


Paste_Image.png

我們對(duì)X軸和Y軸比較熟悉了,分別以右和下為正方向魔慷,Z軸和這兩個(gè)軸分別垂直只锭,指向視角外為正方向:


Paste_Image.png

由圖所見,繞Z軸的旋轉(zhuǎn)等同于之前二維空間的仿射旋轉(zhuǎn)院尔,但是繞X軸和Y軸的旋轉(zhuǎn)就突破了屏幕的二維空間蜻展,并且在用戶視角看來(lái)發(fā)生了傾斜。
  • CATransform3D應(yīng)用

CATransform3D類型的矩陣邀摆,和Core Graphics的函數(shù)類似纵顾,但是3D的平移和旋轉(zhuǎn)多處了一個(gè)z參數(shù),并且旋轉(zhuǎn)函數(shù)除了angle之外多出了x,y,z三個(gè)參數(shù)栋盹,分別決定了每個(gè)坐標(biāo)軸方向上的旋轉(zhuǎn):

/* 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);

舉個(gè)栗子施逾,點(diǎn)擊屏幕將子視圖旋轉(zhuǎn)-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

運(yùn)行效果:


running.gif

看起來(lái)好像只是寬度被壓扁了而已,這是因?yàn)槲覀冊(cè)谟靡粋€(gè)斜向的視角看它,而不是透視汉额。

  • 透視效果

CATransform3D的透視效果通過(guò)一個(gè)矩陣中一個(gè)很簡(jiǎn)單的元素來(lái)控制:m34曹仗。m34用于按比例縮放X和Y的值來(lái)計(jì)算到底要離視角多遠(yuǎn),其默認(rèn)值是0蠕搜,我們可以通過(guò)設(shè)置m34為-1.0 / d來(lái)應(yīng)用透視效果怎茫,d代表了想象中視角相機(jī)和屏幕之間的距離,以像素為單位妓灌,那應(yīng)該如何計(jì)算這個(gè)距離呢轨蛤?實(shí)際上并不需要如何計(jì)算,大概估算一個(gè)就好了虫埂,一般是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

運(yùn)行效果:


running.gif
  • 滅點(diǎn)

當(dāng)在透視角度繪圖的時(shí)候,遠(yuǎn)離相機(jī)視角的物體將會(huì)變小變遠(yuǎn)告丢,當(dāng)遠(yuǎn)離到一個(gè)極限距離枪蘑,它們可能就縮成了一個(gè)點(diǎn),于是所有的物體最后都匯聚消失在同一個(gè)點(diǎn)岖免,這個(gè)點(diǎn)叫滅點(diǎn)岳颇。

Core Animation定義了滅點(diǎn)位于變換圖層的anchorPoint,這就是說(shuō)颅湘,當(dāng)圖層發(fā)生變換時(shí)话侧,這個(gè)點(diǎn)永遠(yuǎn)位于圖層變換之前anchorPoint的位置。當(dāng)改變一個(gè)圖層的position闯参,你也改變了它的滅點(diǎn)瞻鹏,做3D變換的時(shí)候要時(shí)刻記住這一點(diǎn),當(dāng)你視圖通過(guò)調(diào)整m34來(lái)讓它更加有3D效果鹿寨,應(yīng)該首先把它放置于屏幕中央新博,然后通過(guò)平移來(lái)把它移動(dòng)到指定位置(而不是直接改變它的position),這樣所有的3D圖層都共享一個(gè)滅點(diǎn)脚草。

  • sublayerTransform

如果有多個(gè)子視圖或者子圖層赫悄,每個(gè)都做3D變換,那就需要分別設(shè)置相同的m34值馏慨,并且確保在變換之前都在屏幕中央共享同一個(gè)position埂淮,這樣操作起來(lái)很麻煩,CALayer有一個(gè)叫做sublayerTransform的屬性可以解決這個(gè)問(wèn)題写隶。

sublayerTransform也是CATransform3D類型倔撞,但和對(duì)一個(gè)圖層的變換不同,它影響到所有的子圖層慕趴,這意味著你可以一次性對(duì)包含這些圖層的容器做變換痪蝇。

使用sublayerTransform鄙陡,滅點(diǎn)被設(shè)置在容器圖層的中點(diǎn),從而不需要再對(duì)子圖層分別設(shè)置了霹俺。這意味著你可以隨意使用position和frame來(lái)放置子圖層柔吼,而不需要把它們放置在屏幕中點(diǎn),然后為了保證統(tǒng)一的滅點(diǎn)用變換來(lái)做平移丙唧。

  • doubleSided

CALayer有一個(gè)叫做doubleSided的屬性來(lái)控制圖層的背面是否要被繪制愈魏,這是一個(gè)BOOL類型,默認(rèn)為YES想际,如果設(shè)置為NO培漏,那么當(dāng)圖層正面從相機(jī)視角消失的時(shí)候,它將不會(huì)被繪制胡本。舉個(gè)栗子:

#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

運(yùn)行效果:


running.gif

3. 固體對(duì)象

現(xiàn)在我們懂得了在3D空間的一些圖層布局的基礎(chǔ)牌柄,接下來(lái)試著創(chuàng)建一個(gè)固態(tài)的3D對(duì)象(實(shí)際上是一個(gè)技術(shù)上所謂的空洞對(duì)象,但它以固態(tài)呈現(xiàn))侧甫,我們用三個(gè)獨(dú)立的視圖來(lái)構(gòu)建一個(gè)殘缺的正方體

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

運(yùn)行效果:

Paste_Image.png

從這個(gè)角度看正方體并不是很明顯珊佣,看起來(lái)只是一個(gè)方塊,為了更好地欣賞它披粟,我們將更換一個(gè)不同的視角

但是旋轉(zhuǎn)這個(gè)正方體將會(huì)顯得很笨重咒锻,因?yàn)槲覀円獑为?dú)對(duì)每個(gè)面做旋轉(zhuǎn),這時(shí)候我們之前說(shuō)的sublayerTransform就派上了用場(chǎng)

添加兩行代碼去旋轉(zhuǎn)containerView圖層的perspective變換矩陣:

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

把相機(jī)(或者相對(duì)相機(jī)的整個(gè)場(chǎng)景)繞Y軸旋轉(zhuǎn)45度守屉,并且繞X軸旋轉(zhuǎn)45度惑艇,現(xiàn)在從另一個(gè)角度去觀察正方體,就能看出它的真實(shí)面貌:

Paste_Image.png

效果出來(lái)了拇泛,但是我還想旋轉(zhuǎn)一下這個(gè)正方體滨巴,看看其他的面,怎么辦呢

so easy俺叭!繞y軸旋轉(zhuǎn)咯

說(shuō)干就干恭取,修改代碼:

#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

跑起來(lái):

running.gif

納尼,這是什么鬼??

這是由于盡管Core Animation圖層存在于3D空間之內(nèi)熄守,但它們并不都存在同一個(gè)3D空間蜈垮。每個(gè)圖層的3D場(chǎng)景其實(shí)是扁平化的,當(dāng)你從正面觀察一個(gè)圖層柠横,看到的實(shí)際上由子圖層創(chuàng)建的想象出來(lái)的3D場(chǎng)景窃款,但當(dāng)你傾斜這個(gè)圖層课兄,你會(huì)發(fā)現(xiàn)實(shí)際上這個(gè)3D場(chǎng)景僅僅是被繪制在圖層的表面牍氛。

總之一句話,圖層是扁平的烟阐,直接把superLayer繞y軸旋轉(zhuǎn)的方法行不通

要想實(shí)現(xiàn)旋轉(zhuǎn)正方體的效果搬俊,就得將所有的子圖層全部挨個(gè)兒做變換

到最后還得用sublayerTransform

繼續(xù)修改代碼:

#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

最終運(yùn)行效果:

running.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末紊扬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子唉擂,更是在濱河造成了極大的恐慌餐屎,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玩祟,死亡現(xiàn)場(chǎng)離奇詭異腹缩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)空扎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門藏鹊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人转锈,你說(shuō)我怎么就攤上這事盘寡。” “怎么了撮慨?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵竿痰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我砌溺,道長(zhǎng)影涉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任抚吠,我火速辦了婚禮常潮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘楷力。我一直安慰自己喊式,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布萧朝。 她就那樣靜靜地躺著岔留,像睡著了一般。 火紅的嫁衣襯著肌膚如雪检柬。 梳的紋絲不亂的頭發(fā)上献联,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音何址,去河邊找鬼里逆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛用爪,可吹牛的內(nèi)容都是我干的原押。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼偎血,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诸衔!你這毒婦竟也來(lái)了盯漂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤笨农,失蹤者是張志新(化名)和其女友劉穎就缆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谒亦,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竭宰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了份招。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羞延。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖脾还,靈堂內(nèi)的尸體忽然破棺而出伴箩,到底是詐尸還是另有隱情,我是刑警寧澤鄙漏,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布嗤谚,位于F島的核電站,受9級(jí)特大地震影響怔蚌,放射性物質(zhì)發(fā)生泄漏巩步。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一桦踊、第九天 我趴在偏房一處隱蔽的房頂上張望椅野。 院中可真熱鬧,春花似錦籍胯、人聲如沸竟闪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)炼蛤。三九已至,卻和暖如春蝶涩,著一層夾襖步出監(jiān)牢的瞬間理朋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工绿聘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗽上,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓熄攘,卻偏偏與公主長(zhǎng)得像兽愤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 很不幸烹看,沒(méi)人能告訴你母體是什么,你只能自己體會(huì) -- 駭客帝國(guó) 在第四章“可視效果”中洛史,我們研究了一些增強(qiáng)圖層和它...
    雪_晟閱讀 408評(píng)論 0 1
  • >*很不幸惯殊,沒(méi)人能告訴你母體是什么,你只能自己體會(huì)* --駭客帝國(guó) 在第四章“可視效果”中也殖,我們研究了一些增強(qiáng)圖層...
    夜空下最亮的亮點(diǎn)閱讀 1,658評(píng)論 0 2
  • Core Animation其實(shí)是一個(gè)令人誤解的命名土思。你可能認(rèn)為它只是用來(lái)做動(dòng)畫的,但實(shí)際上它是從一個(gè)叫做Laye...
    小貓仔閱讀 3,707評(píng)論 1 4
  • 本文轉(zhuǎn)載自:http://www.cocoachina.com/ios/20150104/10816.html 為...
    idiot_lin閱讀 634評(píng)論 0 1
  • 今天研究圖形變換 仿射變換 在第三章“圖層幾何學(xué)”中忆嗜,我們使用了UIView的transform屬性旋轉(zhuǎn)了鐘的指針...
    李紹旌閱讀 264評(píng)論 0 0