如何使用 CATransform3D 處理 3D 影像、製做互動立體旋轉的效果


今天讓我們討論一下在iOS App的立體影像處理,而要講這題目就不可不講解 – CATransform3DCATransform3D 是一個用來處理 3D 影像,像是旋轉、縮放、平移等 3D 影像的控制。CATransform3D 是採用三維坐標系統,x 軸向下為正,y 向右為正,z 軸則是垂直於螢幕向外為正。

你可以這樣理解CATransform3D ,它本身就是一個 4×4 的矩陣:

Matrix

在這邊,我們不需要了解矩陣中每一個數字是什麼意思,因為在 CATransform3D 中已經有方法可以處理大部分的功能:

  • CATransform3DTranslate
  • CATransform3DRotate
  • CATransform3DInvert
  • CATransform3DScale
  • CATransform3DAffine

如果只說道理,相信很難理解CATransform3D的運作,最好還是實作一個簡單的範例程式以闡釋當中的原理。在這篇,我要教大家如何做出一個可以被轉動的骰子,下圖就是完成品:

Dice

開始建立範例App

如常開啟Xcode並使用Single View Application建立一個新項目。在Main.storyboard,先在View Controller 做一個簡單的 View,將背景顏色轉為藍色(或其他顏色也可以)。

rotatedice-storyboard

ViewController 加入IBOutlet,並將之連接至新建立的View:

如何將View變得立體

現在要加入一個方法讓 View (即blueView) 以 y 軸為中心旋轉 45度,CATransform3D 的方法都是輸入一個矩陣去改變內容。以 CATransform3DRotate 為例,第一個參數是定義好的矩陣,之後再用角度與 x、y、z 所形成的向量做旋轉:

之後,再在viewDidLoad()方法呼叫viewTransform()

現在可先試試執行程式,你會發覺那個View實際上旋轉之後並沒有 3D 的感覺,只是變瘦的 View 。

view controller's view

在真實世界中,當物體遠離我們時,由於視角的關係會看起來變小,理論上來說,比較遠的邊會比近的邊要短,但是實際上對系統來說他們是一樣長的,所以我們要做一些修正。透過設置 CATransform3D 的 m34 為 -1.0 / d 來讓影像有遠近的 3D 效果,d 代表了想像中視角與螢幕的距離,這個距離只需要大概估算,不需要很精準的計算。

解說:m34 用於按比例縮放 x 和 y 的值來呈現視角的遠近。

現在修改viewTransform()並加入一行程式碼以設置m34

完成後,再試試執行App看看效果如何。現在那個應該有一種立體的感覺:

View3

手勢控制

接下來我們要改為手勢控制這個 View 的角度,在ViewController類別先加一個angle變數:

之後,修改viewTransform方法:

另外,因為我們會用UIPanGestureRecognizer來識別手勢,viewDidLoad也作出相應的修改:

在這邊用 CATransform3DRotate 的方法對 X 軸與 Y 軸做轉動,以手勢在 View 上每次移動的相對座標為基準(即是sender.translation(in: blueView)),直接去更改整個 View 的翻轉角度。

又再試一試執行App,嘗試用滑鼠轉動View,你會發現非常失控!

dice-rotation-crazy

因為判定旋轉角度的依據,就是手勢在這個 View 上面移動的位置,而 View 在轉動的時候座標會隨著旋轉而不停的變動,以至於手勢無法準確的控制,所以要拿一個不會動的物件當作基準,並且不能因為轉動而改變可控的面積,解決辦法如下(改動的程式碼以黃色標示):

View 需要用手勢移動當作依據,所以不直接對這個 View 做旋轉,而是旋轉 View 裡面的 sublayer,layer 裡面的有個方法可以實作這個功能 sublayerTransform ,並把內容以 subView 的方式加入,然後把 blueView 的 backgroundColor 拿掉,這樣就能很正常的轉動了。

FirstRotate

將普通的View變成骰子

接下來,就是把這個 blueView 換成骰子。然而骰子並不是平面,是一個立體的物件,那要如何在平面上做出一個立體的物件?在這邊要利用 CATransform3DTranslateCATransform3DRotate 來做出立體物件的效果,首先做骰子的 1、2、3 點。

現在先下載骰子的圖像,並將所有的圖加進Xcode項目。我們會先把 View 修改成 1 點,順便修改 View 的名稱。先將原本的blueView從storyboard 删除,之後在ViewController建立diceView

將以下viewDidLoad以及viewTransform方法更新至如下,同時加入一個新方法addDice

我們在addDice建立相等於blue view的view並同時載入1點的圖像,如你試執行程式,應該會得到以下的效果:

OneRotate

加入骰子第 2、3 點,並分別垂直於 1 點:

又再試一試效果,看來不錯!但還是不太像一顆骰子。

3SideOrigin

要解決這問題,我們就要平移每一個 imageView。CATransform3DRotate 不是只有轉了影像,而是整個座標系統,所以只需要每個面的 z 軸都增加(減少) 50 就能夠做一半的正方體:

現在再試試執行程式,應該有了三個面!

3Side

剩下的就是把三個對應的面加進去,就能完成一顆骰子:

在這邊要注意的是 1 跟 6 都沒有旋轉,所以要一正一負。好了!終於完成,現在執行程就會有一顆會旋轉骰子。

Dice

希望你透過這個實作對 CATransform3D 有所了解,如有問題,請留言給我。另外,你可以在這裡下載完整的範例項目


iOS開發工程師,對於嘗試新的技術很有興趣,也常常分享學習經驗,並喜歡把想到的東西實作出來。除了工作之外也喜歡閱讀哲學、心理學相關書籍。歡迎研究討論,可以透過email聯絡我 [email protected]

blog comments powered by Disqus
訂閲電子報

訂閲電子報

AppCoda致力於發佈優質iOS程式教學,你不必每天上站,輸入你的電子郵件地址訂閱網站的最新教學文章。每當有新文章發佈,我們會使用電子郵件通知你。

已收你的指示。請你檢查你的電郵,我們已寄出一封認證信,點擊信中鏈結才算完成訂閱。

Shares
Share This