iOS核心動畫筆記5-變換

變換

這一節主要講的是, 對圖層進行旋轉,縮放扭曲等操做.app

1. 仿射變換

在視圖層面上, UIView有個屬性叫作 transform, 能夠進行二維層面上的圖層變換. 主要包括: 旋轉/平移/縮放操做.框架

當圖層應用變換矩陣進行變換時候, 圖層矩形內的每一個點都會被相應的進行變化, 從而造成一個新的四邊形的形狀. 仿射變換中"仿射"的意思是不管變換矩陣用什麼值, 圖層中平行的兩條線在變換以後仍然保持平行.ide

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變換的屬性.3d

弧度和角度的轉換code

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

3. 混合變換

經過CG框架咱們能夠簡單的建立不一樣的仿射變換, 若是咱們須要縮放的同時又要旋轉, 這時候就用到了混合變換, CG框架也提供了豐富的框架, 很簡單, 不解釋:orm

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, 緣由是縮放時候, 平移也會被縮放掉.blog

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

相關文章
相關標籤/搜索