分享一個自己做的3d立方體(在瑞士航空見到過類似的這種首頁),還可以添加以下要素變得更加完善:
1)旋轉過程中每個面的透明度可以更改
2)手勢滑動結束可以添加慣性轉動動畫
效果如圖:
直接上代碼了 ,直接全粘貼到一個swift檔案裡既可以使用
1.聲明變量 公共函數
import UIKit
class CubeMenuView: UIView {
//立方體四個面
var sidesArray: [UIImageView]!
//立方體上面
var topImageView: UIImageView!
//立方體下面陰影
var shadowImageView: UIImageView!
//立方體每個面的長寬
var cubeWidth: CGFloat = 0.0
//記錄手勢開始點
var beginPoint: CGPoint?
//手勢停止時x軸移動的距離
var moveX: CGFloat = 0.0
//每個面移動的角度
var moveAngle: CGFloat = 0.0
//每個面目前的角度
var currentAngle: CGFloat = 0.0
//最前面一個面的index
var currentIndex: UInt = 1
//整個立方體沿X軸的傾斜角度
let kCubeInclinedAngle_Y: CGFloat = -CGFloat(M_PI_4 / 4.0)
let distanceZ: CGFloat = 1000.0
func CATransform3DMakePerspective(center: CGPoint, disZ: CGFloat) -> CATransform3D{
let transToCenter = CATransform3DMakeTranslation(-center.x, -center.y, 0)
let transBack = CATransform3DMakeTranslation(center.x, center.y, 0)
var scale = CATransform3DIdentity
scale.m34 = -1.0 / disZ
return CATransform3DConcat(CATransform3DConcat(transToCenter, scale), transBack)
}
func CATransform3DPerspect(t: CATransform3D, center: CGPoint, disZ: CGFloat) -> CATransform3D {
return CATransform3DConcat(t, CATransform3DMakePerspective(center, disZ: disZ))
}
func randomColor() -> UIColor {
var h = CGFloat(Double(arc4random() % 256) / 256.0)
var s = CGFloat(Double(arc4random() % 128) / 256.0) + 0.5
var b = CGFloat(Double(arc4random() % 128) / 256.0) + 0.5
return UIColor(hue: h, saturation: s, brightness: b, alpha: 1.0)
}
2.初始化
init(frame: CGRect, fourImagesArray: [UIImage?], topImage: UIImage?, shadowImage: UIImage?, width: CGFloat) {
super.init(frame: frame)
if (topImage != nil) {
topImageView = UIImageView(image: topImage)
} else {
topImageView = UIImageView()
topImageView.backgroundColor = randomColor()
}
if (shadowImage != nil) {
shadowImageView = UIImageView(image: shadowImage)
} else {
shadowImageView = UIImageView()
shadowImageView.backgroundColor = randomColor()
}
sidesArray = Array()
for (var i = 0; i < 4; i++){
let imgView = UIImageView()
imgView.backgroundColor = randomColor()
if fourImagesArray.count == 4 {
imgView.image = fourImagesArray[i]
}
sidesArray.append(imgView)
}
cubeWidth = width
moveAngle = 0.0
currentAngle = 0.0
currentIndex = 1
addViews()
showCube()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
3.添加各個view
func addViews(){
//所有的6個層面開始時都位于中心點
var frame = CGRectMake(CGRectGetMidX(bounds) - cubeWidth / 2, CGRectGetMidY(bounds) - cubeWidth / 2, cubeWidth, cubeWidth)
topImageView.frame = frame
addSubview(topImageView!)
for imgView in sidesArray {
imgView.frame = frame
addSubview(imgView)
}
frame.origin.y += cubeWidth / 2 + 30
shadowImageView.frame = frame
addSubview(shadowImageView)
}
4.旋轉view組合成立方體
//旋轉各個面形成一個立方體
func showCube() {
let upT = CATransform3DMakeTranslation(0, 0, cubeWidth / 2)
let downT = CATransform3DMakeTranslation(0, 0, -cubeWidth / 2)
var rotationZ = CATransform3DMakeRotation(-moveAngle, 0, 0, 1)
var rotationX = CATransform3DMakeRotation(kCubeInclinedAngle_Y + CGFloat(M_PI_2), 1, 0, 0);
var topRotate = CATransform3DConcat(CATransform3DConcat(rotationZ, upT), CATransform3DConcat(rotationX, downT))
topImageView.layer.transform = CATransform3DPerspect(topRotate, center: CGPointZero, disZ: distanceZ)
var shadowT = CATransform3DConcat(rotationZ, CATransform3DMakeRotation(kCubeInclinedAngle_Y + CGFloat(M_PI_2 - M_PI_4 / 5), 1, 0, 0))
shadowImageView.layer.transform = CATransform3DPerspect(shadowT, center: CGPointZero, disZ: distanceZ)
let commonRotationX = CATransform3DMakeRotation(kCubeInclinedAngle_Y, 1, 0, 0)
var rotation: CATransform3D
for var i = 0; i < sidesArray.count; i++ {
let imgView = sidesArray[i]
rotation = CATransform3DMakeRotation(CGFloat(M_PI_2 * Double(i)) + moveAngle, 0, 1, 0)
var mat = CATransform3DConcat(CATransform3DConcat(upT, rotation), CATransform3DConcat(commonRotationX, downT))
imgView.layer.transform = CATransform3DPerspect(mat, center: CGPointZero, disZ: distanceZ)
}
}
5.手勢動作
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let allTouches = event.allTouches()
if allTouches?.count == 1 {
moveX = 0
beginPoint = (touches as NSSet).anyObject()?.locationInView(self)
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
let allTouches = event.allTouches()
if allTouches?.count == 1 {
let currentPoint = (touches as NSSet).anyObject()?.locationInView(self)
moveX = currentPoint!.x - beginPoint!.x
//旋轉360度時置0
let oneLap = CGFloat(M_PI * 2);
if (fabs(moveAngle) >= oneLap) {
moveAngle = 0;
currentAngle = 0;
}
moveAngle = currentAngle + moveX / 320.0 * CGFloat(M_PI)
showCube()
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
let allTouches = event.allTouches()
if allTouches?.count == 1 {
let currentPoint = (touches as NSSet).anyObject()?.locationInView(self)
let endX = currentPoint!.x - beginPoint!.x
if fabs(endX) > 0 {
//轉動
currentAngle = moveAngle
//顯示正确的一面
showRightSide()
} else {
//點選
var alertView = UIAlertView(title: "提示", message: "點選了第\(currentIndex)面", delegate: nil, cancelButtonTitle: "确定")
alertView.show()
}
}
}
6.手勢停止後的校正立方體
//根據目前立方體旋轉的角度判斷哪個面應該在最前面
func showRightSide() {
let stopAngle = fabs(currentAngle)
if (stopAngle > CGFloat(7 * M_PI_4) || stopAngle <= CGFloat(M_PI_4)) {
//第一個面
moveAngle = 0.0
currentIndex = 1
} else if (stopAngle > CGFloat(M_PI_4) && stopAngle <= CGFloat(3 * M_PI_4)) {
if (moveAngle >= 0) {
//第四個面
moveAngle = CGFloat(M_PI_2)
currentIndex = 4
} else {
//第二個面
moveAngle = CGFloat(3 * M_PI / 2)
currentIndex = 2
}
} else if (stopAngle > CGFloat(3 * M_PI_4) && stopAngle <= CGFloat(5 * M_PI_4)){
//第三個面
moveAngle = CGFloat(M_PI)
currentIndex = 3
} else if (stopAngle > CGFloat(5 * M_PI_4) && stopAngle <= CGFloat(7 * M_PI_4)){
//第二個面
moveAngle = CGFloat(3 * M_PI / 2)
currentIndex = 2
}
currentAngle = moveAngle
//動畫旋轉立方體
UIView.animateWithDuration(0.3, animations: { () -> Void in
self.showCube()
})
}
測試代碼:随意找個view初始化添加即可,用圖檔效果更好
let menuView = CubeMenuView(frame: CGRectMake(10, 100, 300, 500), fourImagesArray: Array(), topImage: nil, shadowImage: nil, width: 180.0)
view.addSubview(menuView)
參考相關資料:http://blog.sina.com.cn/s/blog_51a995b70101mz3q.html