很不幸,沒人能告訴你母體是什麼,你只能本身體會 -- 駭客帝國 git
在第四章「可視效果」中,咱們研究了一些加強圖層和它的內容顯示效果的一些技術,在這一章中,咱們將要研究能夠用來對圖層旋轉,擺放或者扭曲的CGAffineTransform,以及能夠將扁平物體轉換成三維空間對象的CATransform3D(而不是僅僅對圓角矩形添加下沉陰影)。 github
在第三章「圖層幾何學」中,咱們使用了UIView的transform屬性旋轉了鐘的指針,但並無解釋背後運做的原理,實際上UIView的transform屬性是一個CGAffineTransform類型,用於在二維空間作旋轉,縮放和平移。CGAffineTransform是一個能夠和二維空間向量(例如CGPoint)作乘法的3X2的矩陣(見圖5.1)。 數組
圖5.1 用矩陣表示的CGAffineTransform和CGPoint app
用CGPoint的每一列和CGAffineTransform矩陣的每一行對應元素相乘再求和,就造成了一個新的CGPoint類型的結果。要解釋一下圖中顯示的灰色元素,爲了能讓矩陣作乘法,左邊矩陣的列數必定要和右邊矩陣的行數個數相同,因此要給矩陣填充一些標誌值,使得既可讓矩陣作乘法,又不改變運算結果,而且不必存儲這些添加的值,由於它們的值不會發生變化,可是要用來作運算。 函數
所以,一般會用3×3(而不是2×3)的矩陣來作二維變換,你可能會見到3行2列格式的矩陣,這是所謂的以列爲主的格式,圖5.1所示的是以行爲主的格式,只要能保持一致,用哪一種格式都無所謂。 atom
當對圖層應用變換矩陣,圖層矩形內的每個點都被相應地作變換,從而造成一個新的四邊形的形狀。CGAffineTransform中的「仿射」的意思是不管變換矩陣用什麼值,圖層中平行的兩條線在變換以後任然保持平行,CGAffineTransform能夠作出任意符合上述標註的變換,圖5.2顯示了一些仿射的和非仿射的變換: spa
圖5.2 仿射和非仿射變換 .net
對矩陣數學作一個全面的闡述就超出本書的討論範圍了,不過若是你對矩陣徹底不熟悉的話,矩陣變換可能會使你感到畏懼。幸運的是,Core Graphics提供了一系列函數,對徹底沒有數學基礎的開發者也可以簡單地作一些變換。以下幾個函數都建立了一個CGAffineTransform實例: 指針
1
2
3
|
CGAffineTransformMakeRotation(CGFloat angle)
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
|
旋轉和縮放變換均可以很好解釋--分別旋轉或者縮放一個向量的值。平移變換是指每一個點都移動了向量指定的x或者y值--因此若是向量表明了一個點,那它就平移了這個點的距離。 code
咱們用一個很簡單的項目來作個demo,把一個原始視圖旋轉45度角度(圖5.3)
圖5.3 使用仿射變換旋轉45度角以後的視圖
UIView能夠經過設置transform屬性作變換,但實際上它只是封裝了內部圖層的變換。
CALayer一樣也有一個transform屬性,但它的類型是CATransform3D,而不是CGAffineTransform,本章後續將會詳細解釋。CALayer對應於UIView的transform屬性叫作affineTransform,清單5.1的例子就是使用affineTransform對圖層作了45度順時針旋轉。
清單5.1 使用affineTransform對圖層旋轉45度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *layerView;
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//rotate the layer 45 degrees
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);
self.layerView.layer.affineTransform = transform;
}
|
注意咱們使用的旋轉常量是M_PI_4,而不是你想象的45,由於iOS的變換函數使用弧度而不是角度做爲單位。弧度用數學常量pi的倍數表示,一個pi表明180度,因此四分之一的pi就是45度。
C的數學函數庫(iOS會自動引入)提供了pi的一些簡便的換算,M_PI_4因而就是pi的四分之一,若是對換算不太清楚的話,能夠用以下的宏作換算:
1
2
|
#define RADIANS_TO_DEGREES(x) ((x)/M_PI*180.0)
#define DEGREES_TO_RADIANS(x) ((x)/180.0*M_PI)
|
Core Graphics提供了一系列的函數能夠在一個變換的基礎上作更深層次的變換,若是作一個既要縮放又要旋轉的變換,這就會很是有用了。例以下面幾個函數:
1
2
3
|
CGAffineTransformRotate(CGAffineTransform t, CGFloat angle);
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy);
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty);
|
當操縱一個變換的時候,初始生成一個什麼都不作的變換很重要--也就是建立一個CGAffineTransform類型的空值,矩陣論中稱做單位矩陣,Core Graphics一樣也提供了一個方便的常量:
1
|
CGAffineTransformIdentity
|
最後,若是須要混合兩個已經存在的變換矩陣,就可使用以下方法,在兩個變換的基礎上建立一個新的變換:
1
|
CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);
|
咱們來用這些函數組合一個更加複雜的變換,先縮小50%,再旋轉30度,最後向右移動200個像素(清單5.2)。圖5.4顯示了圖層變換最後的結果。
清單5.2 使用若干方法建立一個複合變換
1
2
3
4
5
6
7
8
9
10
|
- (void)viewDidLoad
{
[super viewDidLoad]; //create a new transform
CGAffineTransform transform = CGAffineTransformIdentity; //scale by 50%
transform = CGAffineTransformScale(transform, 0.5, 0.5); //rotate by 30 degrees
transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0); //translate by 200 points
transform = CGAffineTransformTranslate(transform, 200, 0);
//apply transform to layer
self.layerView.layer.affineTransform = transform;
}
|
圖5.4 順序應用多個仿射變換以後的結果
圖5.4中有些須要注意的地方:圖片向右邊發生了平移,但並無指定距離那麼遠(200像素),另外它還有點向下發生了平移。緣由在於當你按順序作了變換,上一個變換的結果將會影響以後的變換,因此200像素的向右平移一樣也被旋轉了30度,縮小了50%,因此它其實是斜向移動了100像素。
這意味着變換的順序會影響最終的結果,也就是說旋轉以後的平移和平移以後的旋轉結果可能不一樣。
Core Graphics爲你提供了計算變換矩陣的一些方法,因此不多須要直接設置CGAffineTransform的值。除非須要建立一個斜切的變換,Core Graphics並無提供直接的函數。
斜切變換是放射變換的第四種類型,較於平移,旋轉和縮放並不經常使用(這也是Core Graphics沒有提供相應函數的緣由),但有些時候也會頗有用。咱們用一張圖片能夠很直接的說明效果(圖5.5)。也許用「傾斜」描述更加恰當,具體作變換的代碼見清單5.3。
圖5.5 水平方向的斜切變換
清單5.3 實現一個斜切變換
@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