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的立方體。



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