UIView的顯示設(shè)置都是對(duì)CALayer屬性的封裝,但是這層封裝掩蓋了CALayer提供的3D顯示功能姑廉。所以我們想讓UIView顯示3D的效果的話,需要直接操作CALayer。
demo地址:https://github.com/smalldu/IOS-Animations
中的3DTransformDemo
看一個(gè)效果圖(一個(gè)靜態(tài) 一個(gè)動(dòng)態(tài))
在CALayer中堡纬,我們使用CATransform3D 對(duì)其進(jìn)行3D變換,其中變換比2d變換多一個(gè)Z軸 均函。 在3D變換中三個(gè)坐標(biāo)軸的旋轉(zhuǎn)方向如圖
發(fā)現(xiàn)沒 繞x軸和y軸轉(zhuǎn)都是垂直于屏幕的升熊。
這里我們創(chuàng)建一個(gè)single view application 。拖一個(gè)view上去涩咖。如圖
然后對(duì)它的layer執(zhí)行3d旋轉(zhuǎn)x軸
@IBOutlet weak var v1:UIView!
override func viewDidLoad() {
super.viewDidLoad()
var trans = CATransform3DIdentity
let angel = CGFloat(M_PI/4)
trans = CATransform3DMakeRotation(angel, 1, 0, 0)
v1.layer.transform = trans
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
效果圖
然而并沒有3D效果 海诲, 只是好像變扁了。怎么回事呢檩互?
這是因?yàn)樘蒯#贑ALayer的顯示系統(tǒng)中,默認(rèn)的相機(jī)使用正交投影闸昨,正交投影沒有遠(yuǎn)小近大效果蚯斯,所以在本例中薄风,只能造成相應(yīng)軸上的縮放。所以不管你旋轉(zhuǎn)多少度都沒有效果
這時(shí)候我們需要用到透視投影拍嵌,CALayer默認(rèn)使用正交投影遭赂,因此沒有遠(yuǎn)小近大效果,而且沒有明確的API可以使用透視投影矩陣横辆。所幸可以通過矩陣連乘自己構(gòu)造透視投影矩陣撇他。
3d變換其實(shí)是一個(gè)矩陣變換
??好暈 , 矩陣太復(fù)雜了狈蚤∧娲猓看不懂怎么辦 。幸運(yùn)的是我們不用研究所有點(diǎn) 炫惩。有一個(gè)很特殊的點(diǎn)m34 僻弹。我們只需要設(shè)置這個(gè)點(diǎn)就可以了
CATransform3D的透視效果通過一個(gè)矩陣中一個(gè)很簡(jiǎn)單的元素來控制:m34
。m34用于按比例縮放X和Y的值來計(jì)算到底要離視角多遠(yuǎn)他嚷。
m34的默認(rèn)值是0蹋绽,我們可以通過設(shè)置m34為-1.0 / d來應(yīng)用透視效果,d代表了想象中視角相機(jī)和屏幕之間的距離筋蓖,以像素為單位卸耘,那應(yīng)該如何計(jì)算這個(gè)距離呢?實(shí)際上并不需要粘咖,大概估算一個(gè)就好了蚣抗。
d的大小大家可以試試
var trans = CATransform3DIdentity
trans.m34 = -1 / 500
let angel = CGFloat(M_PI/4)
trans = CATransform3DMakeRotation(angel, 1, 0, 0)
v1.layer.transform = trans
效果
trans.m34 = -1 / 20
效果
trans.m34 = -1/3000
效果
d代表了相機(jī)離它的距離,這個(gè)度自己感覺瓮下。一般500-1000效果挺好
下面我們就利用這個(gè)3d變換做一個(gè)立方體 翰铡。這個(gè)要一點(diǎn)空間想象力
首先創(chuàng)建一個(gè)項(xiàng)目拖6個(gè)view都居中,放到一個(gè)容器中
然后再利用3d變換畫六個(gè)面讽坏,Container view決定視野,然后根據(jù)手勢(shì)決定視野角度
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var containerV:UIView!
@IBOutlet weak var v1:UIView!
@IBOutlet weak var v2:UIView!
@IBOutlet weak var v3:UIView!
@IBOutlet weak var v4:UIView!
@IBOutlet weak var v5:UIView!
@IBOutlet weak var v6:UIView!
var angel = CGFloat(-M_PI/4)
override func viewDidLoad() {
super.viewDidLoad()
var trans = CATransform3DIdentity
trans.m34 = -1 / 500
var trans2 = CATransform3DTranslate(trans, 0, 0, 50)
v1.layer.transform = trans2
trans2 = CATransform3DTranslate(trans, 50, 0, 0)
trans2 = CATransform3DRotate(trans2, CGFloat(M_PI_2) , 0 , 1 , 0);
v2.layer.transform = trans2
trans2 = CATransform3DTranslate(trans, 0, -50, 0)
trans2 = CATransform3DRotate(trans2, CGFloat(M_PI_2) , 1 , 0 , 0);
v3.layer.transform = trans2
trans2 = CATransform3DTranslate(trans, 0, 50, 0)
trans2 = CATransform3DRotate(trans2, CGFloat(-M_PI_2) , -1 , 0 , 0);
v4.layer.transform = trans2
trans2 = CATransform3DTranslate(trans, -50, 0, 0)
trans2 = CATransform3DRotate(trans2, CGFloat(-M_PI_2) , 0 , 1 , 0);
v5.layer.transform = trans2
trans2 = CATransform3DTranslate(trans, 0, 0, -50)
trans2 = CATransform3DRotate(trans2, CGFloat(M_PI) , 0 , 1 , 0);
v6.layer.transform = trans2
trans = CATransform3DRotate(trans, angel , 0, 1 , 0);
trans = CATransform3DRotate(trans, angel , 1, 0 , 0);
containerV.layer.sublayerTransform = trans
let pan = UIPanGestureRecognizer(target: self, action: "handlePan:")
containerV.addGestureRecognizer(pan)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func handlePan(sender:UIPanGestureRecognizer){
let p = sender.translationInView(containerV)
let angel1 = angel+(p.x/30)
let angel2 = angel-(p.y/30)
var trans = CATransform3DIdentity
trans.m34 = -1 / 500
trans = CATransform3DRotate(trans, angel1 , 0, 1 , 0);
trans = CATransform3DRotate(trans, angel2 , 1, 0 , 0);
containerV.layer.sublayerTransform = trans
print("拖動(dòng)\(p)")
}
}
最終效果圖就是上面的gif 锭魔。 大家可以去git上下載源碼運(yùn)行下。效果還不錯(cuò)