例子代碼可以在 下載下傳
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空間内的平移、縮放、旋轉等變化。
第一個例子,繞坐标軸的旋轉:
原始場景如圖
使用 image.layer.transform = catransform3dmakerotation(m_pi/6, 0, 0, 1);
繞z軸旋轉30度後的效果
使用 image.layer.transform = catransform3dmakerotation(m_pi/6,
0, 1, 0); 繞y軸旋轉30度後的效果
1, 0, 0); 繞x軸旋轉30度後的效果
能夠發現,繞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平面(也可以了解為螢幕)的距離。
帶透視效果的旋轉,效果如下:
catransform3d rotate
= catransform3dmakerotation(m_pi/6, 1, 0, 0);
image.layer.transform = catransform3dperspect(rotate, cgpointmake(0, 0), 200);
= catransform3dmakerotation(m_pi/6, 0,
1, 0);
= catransform3dmakerotation(m_pi/6, 0, 0,
1);
image的預設anchorpoint為(0.5,0.5),也就是在圖檔中心;眼睛在圖檔中心點,距螢幕200機關。可以觀察到,因為翻轉,使圖檔的不同部分離螢幕距離不同,近大遠小的效果使立體感大大提升。饒z軸的旋轉不因為透視産生變化,因為所有的點離螢幕距離相同,是以不會産生近大遠小的透視感。
第三,更多的效果。calayer的旋轉和縮放是繞anchorpoint點的,改變anchorpoint的值,可以使layer繞不同的點而不隻是中心點旋轉縮放。在構造透視投影矩陣的例子中就可以看到,catransform3d可以使用catransform3dconcat函數連接配接起來以構造更複雜的變換。通過這些方法,可以組合出更多的效果來。下面是個翻轉的動畫。使用uitimer
-
(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);
最後是兩個更複雜的例子。第一個是使用四張同樣大小的圖檔圍成一個框,讓這個框動畫旋轉。
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及子類上,下面是個普通界面的立體翻轉效果。
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);