CoreAnimation之變換

CoreAnimation之CALayer基礎(chǔ)

1. CGAffineTransform

  • CGAffineTransform基礎(chǔ)

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

Paste_Image.png

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

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

  • CGAffineTransform應(yīng)用

CGAffineTransform的前綴CG即Core Graphics,Core Graphics提供了一系列函數(shù),對完全沒有數(shù)學(xué)基礎(chǔ)的開發(fā)者也能夠簡單地做一些變換。如下幾個(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

π是弧度單位,其對應(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è)栗子,我們來用這些函數(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ā)生了平移,但并沒有指定距離那么遠(yuǎn)(200像素),另外它還有點(diǎn)向下發(fā)生了平移。原因在于當(dāng)你按順序做了變換,上一個(gè)變換的結(jié)果將會(huì)影響之后的變換,所以200像素的向右平移同樣也被旋轉(zhuǎn)了30度,縮小了50%,所以它實(shí)際上是斜向移動(dòng)了100像素。這意味著變換的順序會(huì)影響最終的結(jié)果,也就是說旋轉(zhuǎn)之后的平移和平移之后的旋轉(zhuǎn)結(jié)果可能不同。


2. CATransform3D

  • CATransform3D基礎(chǔ)

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


Paste_Image.png

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


Paste_Image.png

由圖所見,繞Z軸的旋轉(zhuǎn)等同于之前二維空間的仿射旋轉(zhuǎn),但是繞X軸和Y軸的旋轉(zhuǎn)就突破了屏幕的二維空間,并且在用戶視角看來發(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

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

  • 透視效果

CATransform3D的透視效果通過一個(gè)矩陣中一個(gè)很簡單的元素來控制:m34。m34用于按比例縮放X和Y的值來計(jì)算到底要離視角多遠(yuǎn),其默認(rèn)值是0,我們可以通過設(shè)置m34為-1.0 / d來應(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,這就是說,當(dāng)圖層發(fā)生變換時(shí),這個(gè)點(diǎn)永遠(yuǎn)位于圖層變換之前anchorPoint的位置。當(dāng)改變一個(gè)圖層的position,你也改變了它的滅點(diǎn),做3D變換的時(shí)候要時(shí)刻記住這一點(diǎn),當(dāng)你視圖通過調(diào)整m34來讓它更加有3D效果,應(yīng)該首先把它放置于屏幕中央,然后通過平移來把它移動(dòng)到指定位置(而不是直接改變它的position),這樣所有的3D圖層都共享一個(gè)滅點(diǎn)。

  • sublayerTransform

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

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

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

  • doubleSided

CALayer有一個(gè)叫做doubleSided的屬性來控制圖層的背面是否要被繪制,這是一個(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. 固體對象

現(xiàn)在我們懂得了在3D空間的一些圖層布局的基礎(chǔ),接下來試著創(chuàng)建一個(gè)固態(tài)的3D對象(實(shí)際上是一個(gè)技術(shù)上所謂的空洞對象,但它以固態(tài)呈現(xiàn)),我們用三個(gè)獨(dú)立的視圖來構(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è)角度看正方體并不是很明顯,看起來只是一個(gè)方塊,為了更好地欣賞它,我們將更換一個(gè)不同的視角

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

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

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

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

Paste_Image.png

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

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

說干就干,修改代碼:

#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空間之內(nèi),但它們并不都存在同一個(gè)3D空間。每個(gè)圖層的3D場景其實(shí)是扁平化的,當(dāng)你從正面觀察一個(gè)圖層,看到的實(shí)際上由子圖層創(chuàng)建的想象出來的3D場景,但當(dāng)你傾斜這個(gè)圖層,你會(huì)發(fā)現(xiàn)實(shí)際上這個(gè)3D場景僅僅是被繪制在圖層的表面。

總之一句話,圖層是扁平的,直接把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)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容