1. 程式人生 > >CoreAnimation程式設計指南之幾何變換

CoreAnimation程式設計指南之幾何變換

 

本章介紹圖層的幾何組成部分,及他們之間的相互關,同時介紹如何變換矩陣可以產生複雜的視覺效果。

 

1.1 圖層的座標系

圖層的座標系在不同平臺上面具有差異性。在iOS系統中,預設的座標系統原點在圖層的中心左上角地方,原點向右和向下為正值。在Mac OS X系統中,預設的座標系原點在圖層的中心左下角地方,原點向右和向上為正值。座標系的所有值都是浮點型別。你在任何平臺上面建立的圖層都採用該平臺預設的座標系。

每個圖層定義並維護自己的座標系,它裡面的全部內容都由此相關的座標系指定位置。該準則同時適應於圖層自己的內容和它的任何子圖層。因為任何圖層定義了它自己的座標系,CALayer類提供相應的方法用於從一個圖層座標系的點、矩形、大小值轉化為另一個圖層座標系相應的值。

一些基於圖層的屬性使用單元座標空間測量它們的值。單元座標空間指定圖層邊界的相對值,而不是絕對值。單元座標空間給定的x和y的值總是在0.0到1.0之間。指定一個沿X軸的值為0.0的點,得到的是圖層左邊緣的一個點,而指定一個1.0的點,則是圖層右邊緣的一個點。(對 Y軸而言,如果是在iOS系統,則0.0對應於頂部的點,而1.0則是底部的點,而在Mac OS X系統,得到的剛好相反,就如之前提到的座標系不同一樣)。而點(0.5,0.5)則剛好是圖層的中心點。

 

1.2 指定圖層的幾何

雖然圖層和圖層樹與檢視和檢視的結構在很多方面具有相似性,但是圖層的幾何卻不同,它更加簡單通俗。圖層的所有幾何屬性,包括圖層的矩陣變換,都可以隱式和顯式動畫。

下圖顯示可以在上下文中指定圖層幾何的屬性:

圖 1  CALayer的幾何屬性

 

 

圖層的position屬性是一個CGPoint的值,它指定圖層相當於它父圖層的位置,該值基於父圖層的座標系。

圖層的bounds屬性是一個CGRect的值,指定圖層的大小(bounds.size)和圖層的原點(bounds.origin)。當你重寫圖層的重畫方法的時候,bounds的原點可以作為圖形上下文的原點。

圖層擁有一個隱式的frame,它是position,bounds,anchorPoint和transform屬性的一部分。設定新的frame將會相應的改變圖層的position和bounds屬性,但是frame本身並沒有被儲存。但是設定新的frame時候,bounds的原點不受干擾,bounds的大小變為frame的大小,即bounds.size=frame.size。圖層的位置被設定為相對於錨點(anchor point)的適合位置。當你設定frame的值的時候,它的計算方式和position、bounds、和anchorPoint的屬性相關。

圖層的anchorPoint屬性是一個CGPoint值,它指定了一個基於圖層bounds的符合位置座標系的位置。錨點(anchor point)指定了bounds相對於position的值,同時也作為變換時候的支點。錨點使用單元空間座標系表示,(0.0,0.0)點接近圖層的原點,而(1.0,1.0)是原點的對角點。改變圖層的父圖層的變換屬性(如果存在的話)將會影響到anchorPoint的方向,具體變化取決於父圖層座標系的Y軸。

當你設定圖層的frame屬性的時候,position會根據錨點(anchorPoint)相應的改變,而當你設定圖層的position屬性的時候,bounds會根據錨點(anchorPoint)做相應的改變。

iOS 注意:以下示例描述基於Mac OS X的圖層,它的座標系原點基於左下角。在iOS上面,圖層的座標系原點位於左上角,原點向下和向右為正值。這變化用具體數值顯示,而不是概念描述。

下圖描述了基於錨點的三個示例值:

 

圖 2  三個錨點值 

 

    anchorPoint預設值是(0.5,0.5),位於圖層邊界的中心點(如上圖顯示),B點把anchorPoint設定為(0.0,0.5)。最後C點(1.0,0.0)把圖層的position設定為圖層frame的右下角。該圖適用於Mac OS X的圖層。在iOS系統裡面,圖層使用不同的座標系,相應的(0.0,0.0)位於左上角,而(1.0,1.0)位於右下角。

    圖層的frame、bounds、position和anchorPoint關係如下圖所示:

 

圖 3  圖層原點 :基於(0.5,0.5) 

 

在該示例中,anchorPoint預設值為(0.5,0.5),位於圖層的中心點。圖層的position值為(100.0,100.0),bounds為(0.0,0.0,120,80.0)。通過計算得到圖層的frame為(40.0,60.0,120.0,80.0)。

如果你新建立一個圖層,則只有設定圖層的frame為(40.0,60.0,120.0,80.0),相應的position屬性值將會自動設定為(100.0,100.0),而bounds會自動設定為(0.0,0.0,120.0,80.0)。

下圖顯示一個圖層具有相同的frame(如上圖),但是在該圖中它的anchorPoint屬性值被設定為(0.0,0.0),位於圖層的左下角位置。

 

圖 4  圖層原點 :基於 (0.0,0.0) 

 

圖層的frame值同樣為(40.0,60.0,120.0,80.0),bounds的值不變,但是圖層的position值已經改變為(40.0,60.0)。

圖層的幾何外形和Cocoa檢視另外一個不同地方是,你可以設定圖層的一個邊角的半徑來把圖層顯示為圓角。圖層的cornerRadius屬性指定了重繪圖層內容,剪下子圖層,繪製圖層的邊界和陰影的時候時候圓角的半徑。

圖層的zPosition屬性值指定了該圖層位於Z軸上面位置,zPosition用於設定圖層相對於圖層的同級圖層的可視位置。

 

1.3 圖層的幾何變換

圖層一旦建立,你就可以通過矩陣變換來改變一個圖層的幾何形狀。CATransform3D的資料結構定義一個同質的三維變換(4x4 CGFloat值的矩陣),用於圖層的旋轉,縮放,偏移,歪斜和應用的透視。

圖層的兩個屬性指定了變換矩陣:transform和sublayerTransform屬性。圖層的transform屬性指定的矩陣結合圖層的anchorPoint屬性作用於圖層和圖層的子圖層上面。圖 3顯示在使用anchorPoint預設值(0.5,0.5)的時候旋轉和縮放變換如何影響一個圖層。而圖 4顯示了同樣的矩陣變換在anchorPoint為(0.0,0.0)的時候如何改變一個圖層。圖層的sublayerTransform屬性指定的矩陣只會影響圖層的子圖層,而不會對圖層本身產生影響。

你可以通過以下的任何一個方法改變CATransform3D的資料結構:

  • 使用CATransform3D函式
  • 直接修改資料結構的成員
  • 使用鍵-值編碼改變鍵路徑

CATransform3DIdentity是單位矩陣,該矩陣沒有縮放、旋轉、歪斜、透視。把該矩陣應用到圖層上面,會把圖層幾何屬性修改為預設值。

 

1.3.1 變換函式

使用變換函式可以在核心動畫裡面在操作矩陣。你可以使用這些函式(如下表)去建立一個矩陣一般後面用於改變圖層或者它的子圖層的transform和sublayerTransform屬性。變換函式或者直接操或者返回一個CATransform3D的資料結構。這可以讓你能夠構建簡單或複雜的轉換,以便重複使用。

表 1  CATransform3D 變換函式 :偏移、旋轉和縮放

Function

Use

CATransform3DMakeTranslation

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].

CATransform3DTranslate

Translate 't' by '(tx, ty, tz)' and return the result: * t' = translate(tx, ty, tz) * t.

CATransform3DMakeScale

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].

CATransform3DScale

Scale 't' by '(sx, sy, sz)' and return the result: * t' = scale(sx, sy, sz) * t.

CATransform3DMakeRotation

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.

CATransform3DRotate

Rotate 't' by 'angle' radians about the vector '(x, y, z)' and return the result. t' = rotation(angle, x, y, z) * t.

 

旋轉的單位採用弧度(radians),而不是角度(degress)。以下兩個函式,你可以在弧度和角度之間切換。

1

2

CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;};

CGFloat RadiansToDegrees(CGFloat radians) {return radians * 180 / M_PI;};

  

核心動畫 提供了用於轉換矩陣的變換函式CATransform3DInvert。一般是用反轉點內轉化物件提供反向轉換。當你需要恢復一個已經被變換了的矩陣的時候,反轉將會非常有幫助。反轉矩陣乘以逆矩陣值,結果是原始值。

變換函式同時允許你把CATransform3D矩陣轉化為CGAffineTransform(仿射)矩陣,前提是CATransform3D矩陣採用如下表示方法。

表 2  CATransform3D 與 CGAffineTransform 轉換

Function

Use

CATransform3DMakeAffineTransform

Returns a CATransform3D with the same effect as the passed affine transform.

CATransform3DIsAffine

Returns YES if the passedCATransform3D can be exactly represented an affine transform.

CATransform3DGetAffineTransform

Returns the affine transform represented by the passedCATransform3D.

 

變換函式同時提供了可以比較一個變換矩陣是否是單位矩陣,或者兩個矩陣是否相等。

表 3  CATransform3D 相等測試

Function

Use

CATransform3DIsIdentity

Returns YES if the transform is the identity transform.

CATransform3DEqualToTransform

Returns YES if the two transforms are exactly equal..

 

1.3.2 修改變換的資料結構

你可以修改CATransform3D的資料結構的元素為任何其他你想要的資料值。程式碼1包含了CATransform3D資料結構的定義,結構的成員都在其相應的矩陣位置。

程式碼 1  CATransform3D structure

1

2

3

4

5

6

7

8

9

struct CATransform3D

{

  CGFloat m11, m12, m13, m14;

  CGFloat m21, m22, m23, m24;

  CGFloat m31, m32, m33, m34;

  CGFloat m41, m42, m43, m44;

};

 

typedef struct CATransform3D CATransform3D;

  

程式碼2中的示例說明了如何配置一個CATransform3D一個角度變換。

程式碼 2  直接修改CATransform3D資料結構

1

2

3

4

CATransform3D aTransform = CATransform3DIdentity;

// the value of zDistance affects the sharpness of the transform.

zDistance = 850;

aTransform.m34 = 1.0 / -zDistance;

  

1.3.3    通過鍵值路徑修改變換

核心動畫擴充套件了鍵-值編碼協議,允許通過關鍵路徑獲取和設定一個圖層的CATransform3D矩陣的值。表4描述了圖層的transform和sublayerTransform屬性的相應關鍵路徑。

表 4  CATransform3D key paths

Field Key Path

Description

rotation.x

The rotation, in radians, in the x axis.

rotation.y

The rotation, in radians, in the y axis.

rotation.z

The rotation, in radians, in the z axis.

rotation

The rotation, in radians, in the z axis. This is identical to setting the rotation.z field.

scale.x

Scale factor for the x axis.

scale.y

Scale factor for the y axis.

scale.z

Scale factor for the z axis.

scale

Average of all three scale factors.

translation.x

Translate in the x axis.

translation.y

Translate in the y axis.

translation.z

Translate in the z axis.

translation

Translate in the x and y axis. Value is an NSSize or CGSize.

你不可以通過Objective-C 2.0的屬性來設定結構域的值,比如下面的程式碼將會無法正常執行:

1

myLayer.transform.rotation.x=0;

 

替換的辦法是,你必須通過setValue:forKeyPath:或者valueForKeyPath:方法,具體如下:

1

[myLayer setValue:[NSNumber numberWithInt:0] forKeyPath:@"transform.rotation.x"];