今天在网到有人实现了一个旋转菜单,我用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的立方体。