這一節主要講的是, 對圖層進行旋轉,縮放扭曲等操做.app
在視圖層面上, UIView有個屬性叫作 transform, 能夠進行二維層面上的圖層變換. 主要包括: 旋轉/平移/縮放操做.框架
當圖層應用變換矩陣進行變換時候, 圖層矩形內的每一個點都會被相應的進行變化, 從而造成一個新的四邊形的形狀. 仿射變換中"仿射"的意思是不管變換矩陣用什麼值, 圖層中平行的兩條線在變換以後仍然保持平行.ide
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)
經過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
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
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);
須要咱們手動的改變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值, 而後再進行變換, 不然無效, 暫時還不知道緣由.
效果:
當在透視角度繪圖時候, 原理相機的屋裏將會越變越小, 當遠到必定的距離, 就縮小成一個點了, 因而全部的物體都聚集消失在一個點, 這個點被稱爲
滅點
. Core Animation定義這個點anchorPoint. 爲了讓全部圖層更有3D效果, 咱們能夠將圖層都放在同一個位置, 也就是 amchorPoint是相同的, 而後經過變換將它移動到指定位置(而不是直接更改Position), 這樣全部的3D圖層就都共享一個滅點了.
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); }
經過3D旋轉, 咱們能夠將圖形旋轉到背面, 進而去觀察圖層的背面. 旋轉180°時候, 會發現是一個正面的鏡像圖片, 就是說圖層是雙面繪製的
. 可是, 既然背面永遠不會被看到, 爲何要去繪製呢???
CALayer有個叫doubleSided
的屬性來控制圖層的背面是否要被繪製. 這是一個BOOL類型, 默認是YES, 若是設置爲NO, 那麼當圖層正面從相機視角消失的時候, 它將不會被繪製.
內圖層向右旋轉45°, 外圖層向左旋轉45°效果:
透視效果+內圖層繞y軸旋轉, 外圖層也繞y軸旋轉, 效果以下:
由於, 儘管Core Animation圖層存在於3D空間以內, 可是它們不屬於同一個3D空間. 每一個圖層的3D場景實際上是扁平化的, 當你從正面觀察一個圖層, 看到的其實是由子圖層建立的想象出來的3D場景, 但當你傾斜時候回發現, 這個3D場景牢牢是被繪製在圖層的表面了.
這個特性使Core Animation建立複雜3D場景變得十分困難, 你不能使用圖層🌲建立一個3D結構的層級關係, 在相同的場景下的任何3D表面必須和一樣的圖層保持一致, 這是由於每一個父視圖都把它的子視圖扁平化了. CALayer有個叫 CATransformLayer
的子類用來解決這個問題, 詳見後文.
測試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; }