天天看點

IOS 3D UI --- CALayer的transform擴充

    例子代碼可以在  下載下傳

  ios的ui是基于uiview類的,我們能看到的每個ui元素都是uiview或者uiview的子類。view按樹形結構組織起來,樹根是uiwindow。

     view負責界面的互動和顯示,其中顯示部分由calayer來完成。每個uiview包含一個calayer執行個體。可以這麼認為,uiview本身是不可見的,我們能看到的都是calayer,uiview隻是負責對calayer進行管理。

uiview的顯示設定都是對calayer屬性的封裝,但是這層封裝掩蓋了calayer提供的3d顯示功能。是以我們想讓uiview顯示3d的效果的話,需要直接操作calayer。

  要操作calayer對象,首先要在工程中包含quartzcore.framework,在檔案中import

<quartzcore/quartzcore.h>頭檔案。quartzcore.framework中包含了calayer以及calayer一些官方子類的定義。

通過設定calayer的transform屬性,可以使calayer産生3d空間内的平移、縮放、旋轉等變化。

  第一個例子,繞坐标軸的旋轉:

    原始場景如圖 

IOS 3D UI --- CALayer的transform擴充

使用 image.layer.transform = catransform3dmakerotation(m_pi/6, 0, 0, 1);

繞z軸旋轉30度後的效果

IOS 3D UI --- CALayer的transform擴充

    使用 image.layer.transform = catransform3dmakerotation(m_pi/6,

0, 1, 0); 繞y軸旋轉30度後的效果

IOS 3D UI --- CALayer的transform擴充

1, 0, 0); 繞x軸旋轉30度後的效果

IOS 3D UI --- CALayer的transform擴充

能夠發現,繞z軸的旋轉比較符合預期,但是繞x軸y軸的旋轉隻是在y軸x軸上進行了一些縮放而已。這是因為,在calayer的顯示系統中,預設的相機使用正交投影,正交投影沒有遠小近大效果,是以在本例中,隻能造成相應軸上的縮放。

  第二個例子,透視投影

calayer預設使用正交投影,是以沒有遠小近大效果,而且沒有明确的api可以使用透視投影矩陣。所幸可以通過矩陣連乘自己構造透視投影矩陣。構造透視投影矩陣的代碼如下:

    catransform3d

catransform3dmakeperspective(cgpoint center, float

disz)

    {

    catransform3d transtocenter =

catransform3dmaketranslation(-center.x, -center.y, 0);

      catransform3d transback =

catransform3dmaketranslation(center.x, center.y, 0);

      catransform3d scale =

catransform3didentity;

    scale.m34 = -1.0f/disz;

    return

catransform3dconcat(catransform3dconcat(transtocenter, scale),

transback);

    }

catransform3dperspect(catransform3d t, cgpoint center, float

    { 

      return catransform3dconcat(t,

catransform3dmakeperspective(center,

disz));

這個函數的實作原理要參考計算機圖形學的3d變換部分,以後再做解釋。現在隻需要了解接口的含義,center指的是相機 的位置,相機的位置是相對于要進行變換的calayer的來說的,原點是calayer的anchorpoint在整個calayer的位置,例如calayer的大小是(100,

200), anchorpoint值為(0.5, 0.5),此時anchorpoint在整個calayer中的位置就是(50,

100),正中心的位置。傳入透視變換的相機位置為(0, 0),那麼相機所在的位置相對于calayer就是(50,

100)。如果希望相機在左上角,則需要傳入(-50,

-100)。disz表示的是相機離z=0平面(也可以了解為螢幕)的距離。

帶透視效果的旋轉,效果如下:

IOS 3D UI --- CALayer的transform擴充

    catransform3d rotate

= catransform3dmakerotation(m_pi/6, 1, 0, 0);

    image.layer.transform = catransform3dperspect(rotate, cgpointmake(0, 0), 200);

IOS 3D UI --- CALayer的transform擴充

= catransform3dmakerotation(m_pi/6, 0,

1, 0);

IOS 3D UI --- CALayer的transform擴充

= catransform3dmakerotation(m_pi/6, 0, 0,

1);

image的預設anchorpoint為(0.5,0.5),也就是在圖檔中心;眼睛在圖檔中心點,距螢幕200機關。可以觀察到,因為翻轉,使圖檔的不同部分離螢幕距離不同,近大遠小的效果使立體感大大提升。饒z軸的旋轉不因為透視産生變化,因為所有的點離螢幕距離相同,是以不會産生近大遠小的透視感。

第三,更多的效果。calayer的旋轉和縮放是繞anchorpoint點的,改變anchorpoint的值,可以使layer繞不同的點而不隻是中心點旋轉縮放。在構造透視投影矩陣的例子中就可以看到,catransform3d可以使用catransform3dconcat函數連接配接起來以構造更複雜的變換。通過這些方法,可以組合出更多的效果來。下面是個翻轉的動畫。使用uitimer

IOS 3D UI --- CALayer的transform擴充

  -

(void)update

      static float angle

= 0;

        angle

+= 0.05f;

catransform3d transloate

= catransform3dmaketranslation(0, 0, -200);

      catransform3d rotate

= catransform3dmakerotation(angle, 0, 1, 0);

      catransform3d mat

= catransform3dconcat(rotate, transloate);

    image.layer.transform = catransform3dperspect(mat, cgpointmake(0, 0), 500);

  最後是兩個更複雜的例子。第一個是使用四張同樣大小的圖檔圍成一個框,讓這個框動畫旋轉。

IOS 3D UI --- CALayer的transform擴充

  catransform3d move

= catransform3dmaketranslation(0, 0, 160);

catransform3d back

= catransform3dmaketranslation(0, 0, -160);

  catransform3d rotate0

= catransform3dmakerotation(-angle, 0, 1, 0);

  catransform3d rotate1

= catransform3dmakerotation(m_pi_2-angle, 0, 1, 0);

  catransform3d rotate2

= catransform3dmakerotation(m_pi_2*2-angle, 0, 1, 0);

  catransform3d rotate3

= catransform3dmakerotation(m_pi_2*3-angle, 0, 1, 0);

catransform3d mat0

= catransform3dconcat(catransform3dconcat(move, rotate0),

back);

catransform3d mat1

= catransform3dconcat(catransform3dconcat(move, rotate1),

catransform3d mat2

= catransform3dconcat(catransform3dconcat(move, rotate2),

catransform3d mat3

= catransform3dconcat(catransform3dconcat(move, rotate3),

image0.layer.transform = catransform3dperspect(mat0, cgpointzero, 500);

image1.layer.transform = catransform3dperspect(mat1, cgpointzero, 500);

image2.layer.transform = catransform3dperspect(mat2, cgpointzero, 500);

image3.layer.transform = catransform3dperspect(mat3, cgpointzero, 500);

 上面的例子都使用uiimage的calayer,但calayer産生的動畫可以應用在所有的uiview及子類上,下面是個普通界面的立體翻轉效果。

IOS 3D UI --- CALayer的transform擴充

    float dis

= 160 * 1.732f;

catransform3d move

= catransform3dmaketranslation(0, 0,

dis);

= catransform3dmaketranslation(0, 0, -dis);

  catransform3d rotate1 = catransform3dmakerotation(-angle

+ m_pi/3.0f, 0, 1, 0);

view0.layer.transform = catransform3dperspect(mat0, cgpointzero, 500);