今天在網到有人實現了一個旋轉菜單,我用CAKeyFrameAnimation(position)和CABasicAnimation(transform.scale)也做了一個,和大家分享下,
菜單支持奇數和偶數個,點擊會旋轉並縮放,圖如下:
實現代碼:都在一個swift即可。
1.先自定義一個UIButton類,添加兩個屬性
import UIKit
class CustomButton: UIButton {
var currentAngle: CGFloat = 0.0
var currentIndex: Int = 0
}
2.添加菜單類,聲明幾個數組分別保存按鈕,按鈕最開始的座標,按鈕最開始的縮放比例
class AnimationMenu: UIView {
var menuArray: [CustomButton] = Array()
var menuSize: CGSize = CGSizeZero
var radius: CGFloat = 0.0
let duration: NSTimeInterval = 0.5
var scaleArray: [CGFloat] = Array()
var menuCenterArray: [CGPoint] = Array()
}
3.初始化函數
init(frame: CGRect, imageArray: [UIImage?], size: CGSize, radius: CGFloat) {
super.init(frame: frame)
menuSize = size
self.radius = radius
createMenuButtons(imageArray)
addMenuViews()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
4.生成按鈕,size已經按照他們的索引進行了相應縮放
func createMenuButtons(array: [UIImage?]) {
//用來計算每個menu的縮放比例
var pArray: [CGFloat] = Array()
let middle: CGFloat = CGFloat(array.count) / 2.0
for var i = 0; i < array.count; i++ {
let oneF = fabs(middle - CGFloat(i))
//5個menu的話裏面保存的是 [2.5, 1.5, 0.5, 0.5, 1.5]
pArray.append(oneF)
}
for var i = 0; i < array.count; i++ {
let middleIndex = fabs(middle - CGFloat(i))
var scale: CGFloat = 1.0
//根據當前menu的索引獲取當前menu的scale,爲0.7的n次方
for var j = 0; j < pArray.count; j++ {
let oneFloat = pArray[j]
if oneFloat == middleIndex {
scale = CGFloat(pow(0.7, Double(j)))
scaleArray.append(scale)
break
}
}
//按照scale參數來進行對應的縮放顯示menu
let button = CustomButton(frame: CGRectMake(0, 0, menuSize.width * scale, menuSize.height * scale))
button.setBackgroundImage(array[i], forState: UIControlState.Normal)
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
button.currentIndex = i
button.tag = 101 + i
menuArray.append(button)
}
}
5.把按鈕按照圓形路徑排列
func addMenuViews() {
//menu中心點組成的圓的半徑
let circle = CAShapeLayer()
let circleCenter = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds))
circle.fillColor = UIColor.clearColor().CGColor
circle.strokeColor = UIColor.grayColor().CGColor
circle.lineWidth = 2.0
circle.path = UIBezierPath(arcCenter: circleCenter, radius: radius, startAngle: 0.0, endAngle: CGFloat(M_PI * 2), clockwise: true).CGPath
//注掉這行可以去除那個圓
layer.addSublayer(circle)
//開始角度爲最下面,也就是90度
let originStartAngle = CGFloat(M_PI * 0.5)
//順時針從圓的最下面一個點開始平均排列
for var i = 0; i < menuArray.count; i++ {
let averageAngle = M_PI * 2 / Double(menuArray.count)
let button = menuArray[i]
//正餘弦求button中心點到圓中心點距離
let dX = CGFloat(sin(averageAngle * Double(i))) * radius
let dY = CGFloat(cos(averageAngle * Double(i))) * radius
let point = CGPointMake(circleCenter.x - dX, circleCenter.y + dY)
button.center = point
menuCenterArray.append(point)
addSubview(button)
//設置當前menu的索引和角度
let endAngle = originStartAngle + CGFloat(Double(i) / Double(menuArray.count) * M_PI * 2.0)
button.currentAngle = endAngle
button.currentIndex = i
}
}
6.點擊最大的按鈕時觸發事件,否則轉動這個按鈕到最大
func buttonClicked(button: CustomButton) {
//currentIndex爲0的是最大的那個
if button.currentIndex == 0 {
let reallyIndex = button.tag - 100
let alert = UIAlertView(title: "alert", message: "點擊了第\(reallyIndex)個menu。", delegate: nil, cancelButtonTitle: "OK")
alert.show()
} else {
showMoveAndScaleAnimation(button)
}
}
7.旋轉+縮放動畫實現
func showMoveAndScaleAnimation(button: CustomButton) {
//圓中心點
let circleCenter = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds))
//每個menu都需要移動的位置個數
let moveCount = menuArray.count - button.currentIndex
for var i = 0; i < menuArray.count; i++ {
let button = menuArray[i]
//menu移動的弧形路徑
let arcPath = CGPathCreateMutable()
//結束角度
let endAngle = button.currentAngle + CGFloat(M_PI * 2.0 / Double(menuArray.count) * Double(moveCount))
CGPathAddArc(arcPath, nil, circleCenter.x, circleCenter.y, radius, button.currentAngle, endAngle, false)
//更改menu座標,通過center數組和currentIndex來更新
let nextIndex = (button.currentIndex + moveCount) % menuArray.count
button.center = menuCenterArray[nextIndex]
//menu弧形移動動畫
let positionAnimation = CAKeyframeAnimation(keyPath: "position")
positionAnimation.path = arcPath
positionAnimation.calculationMode = "paced"
//menu縮放動畫
button.frame.size = CGSizeMake(menuSize.width, menuSize.height)
let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
scaleAnimation.fromValue = scaleArray[button.currentIndex]
scaleAnimation.toValue = scaleArray[nextIndex]
//組合兩個動畫
let animationGroup = CAAnimationGroup()
animationGroup.animations = [positionAnimation, scaleAnimation]
animationGroup.fillMode = kCAFillModeForwards
animationGroup.removedOnCompletion = false
animationGroup.duration = NSTimeInterval(CGFloat(moveCount) / 4.0) + duration
animationGroup.autoreverses = false
button.layer.addAnimation(animationGroup, forKey: "animationGroup")
//更新menu的當前索引和當前角度
button.currentIndex = nextIndex
button.currentAngle = endAngle
}
}
測試代碼:
let array = [UIImage(named: "image1"), UIImage(named: "image2"), UIImage(named: "image3"), UIImage(named: "image4"), UIImage(named: "image5")]
let progessView = AnimationMenu(frame: CGRectMake(50, 100, 300, 300), imageArray: array, size: CGSizeMake(100, 100), radius:90)
view.addSubview(progessView)
over,具體的實現並不難,我寫的比較囉嗦,請大家見諒。下一篇準備用swift寫一個3D的立方體。