iOS编程——Swift实现一个3D圆型旋转菜单

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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章