iOS編程——Swift實現多段CAShapeLayer動畫

今天看了一個例子,按照自己的思路用swift寫了一個,跟大家分享下。執行完的結果如下(剛開始不知道怎麼傳動態圖 - -!):



這個實現的效果是  每點擊一下屏幕,就會出現一段隨機顏色的圓環接着之前的圓環旋轉


實現代碼:


1.新建swift文件,繼承於UIView,內容如下:

import UIKit

class CustomProgessView: UIView {
    
}

2.聲明:聲明所有的全局變量

    //圓環線寬
    let lineWidth = 50.0
    //整個圓環完成所需時間
    let duration:CGFloat = 5.0
    
    //圓環是否全部完成
    var circleComplete = false
    
    //背景灰色圓環
    var backgroundLayer: CAShapeLayer!
    
    //所有圓環段的集合
    var layerArray: [CAShapeLayer]!


3.初始化,添加灰色背景圓環:

override init(frame: CGRect) {
        super.init(frame: frame)
        initParam()
        
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initParam()
    }
    
    func initParam() {
        layerArray = Array()
        
        addBackgroundCircle()
    }
    
    func addBackgroundCircle() {
        backgroundLayer = CAShapeLayer()
        backgroundLayer.fillColor = UIColor.clearColor().CGColor
        backgroundLayer.strokeColor = UIColor.grayColor().CGColor
        backgroundLayer.lineWidth = CGFloat(lineWidth)
        layer.addSublayer(backgroundLayer)
        
        var backgroundPath = UIBezierPath(ovalInRect: self.bounds)
        backgroundLayer.path = backgroundPath.CGPath
    }

4.生成一段圓環CAShapeLayer 函數

    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)
    }
    
    func createOneLayer(){

        var progessLayer = CAShapeLayer()
        progessLayer.fillColor = UIColor.clearColor().CGColor
        progessLayer.strokeColor = randomColor().CGColor
        progessLayer.lineWidth = backgroundLayer.lineWidth
        
        progessLayer.strokeStart = 0.0
        progessLayer.strokeEnd = 0.0
        
        progessLayer.path = backgroundLayer.path
        layer.addSublayer(progessLayer)
        
        layerArray.append(progessLayer)
    }

5.開啓觸摸函數,每點擊一下屏幕就要生成一個圓環

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        if circleComplete == false{
            createOneLayer()
            showLayerAnimation()
        }
    }

6.顯示每一個圓環的動畫,

重點來了,這裏因爲不但要同時顯示Layer動畫,還要同時清除圓環的舊Layer,用到keyPath="strokeStart"和keyPath="strokeEnd"

/*
    1 keyPath = strokeStart  動畫的fromValue = 0,toValue = 1
    表示從路徑的0位置畫到1 怎麼畫是按照清除開始的位置也就是清除0 一直清除到1 效果就是一條路徑慢慢的消失
    
    2 keyPath = strokeStart  動畫的fromValue = 1,toValue = 0
    表示從路徑的1位置畫到0 怎麼畫是按照清除開始的位置也就是1 這樣開始的路徑是空的(即都被清除掉了)一直清除到0 效果就是一條路徑被反方向畫出來
    
    3 keyPath = strokeEnd  動畫的fromValue = 0,toValue = 1
    表示 這裏我們分3個點說明動畫的順序  strokeEnd從結尾開始清除 首先整條路徑先清除後2/3,接着清除1/3 效果就是正方向畫出路徑
    
    4 keyPath = strokeEnd  動畫的fromValue = 1,toValue = 0
    效果就是反方向路徑慢慢消失
*/
要點:

(1)新添加的那個圓環因爲fromValue是從0.0開始,所以不需要清除。其他的所有圓環,做繼續做toValue=1.0的同時,還需要同時清除從strokeStart到strokeEnd的舊Layer。

  (2)同時還要注意每添加一個圓環段,都需要重新計算剩下動畫進度的時間

    func showLayerAnimation(){
        
        for oneProgessLayer in layerArray {
            
            //動畫時間爲剩餘路徑的動畫時間
            let leftAnimationDuration = duration * (1.0 - oneProgessLayer.strokeEnd)
            //公共動畫 fromValue是上一次的toValue,toValue都爲1
            var commonAnimation = CABasicAnimation(keyPath: "strokeEnd")
            commonAnimation.duration = NSTimeInterval(leftAnimationDuration)
            commonAnimation.fromValue = oneProgessLayer.strokeEnd
            commonAnimation.toValue = 1.0
            commonAnimation.fillMode = kCAFillModeForwards
            commonAnimation.removedOnCompletion = false
            oneProgessLayer.addAnimation(commonAnimation, forKey: "strokeEnd")
            
            //除了最新添加的那個Layer,其他都需要清除舊的Layer
            if  layerArray.last !== oneProgessLayer {
                var dissaperAnimation = CABasicAnimation(keyPath: "strokeStart")
                dissaperAnimation.duration = NSTimeInterval(leftAnimationDuration)
                dissaperAnimation.fromValue = oneProgessLayer.strokeStart
                dissaperAnimation.toValue = 1.0 - oneProgessLayer.strokeEnd
                dissaperAnimation.fillMode = kCAFillModeForwards
                dissaperAnimation.removedOnCompletion = false
                oneProgessLayer.addAnimation(dissaperAnimation, forKey: "strokeStart")
            }
        }
        
        //背景灰色Layer一直在清除,清除完畢圓環即繪製完成
        var bgDuration = duration * (1.0 - backgroundLayer.strokeStart)

        var bgAnimation = CABasicAnimation(keyPath: "strokeStart")
        bgAnimation.duration = NSTimeInterval(bgDuration)
        bgAnimation.fromValue = backgroundLayer.strokeStart
        bgAnimation.removedOnCompletion = false
        bgAnimation.toValue = 1.0
        bgAnimation.delegate = self
        backgroundLayer.addAnimation(bgAnimation, forKey: "otherStrokeEnd")
        
    }

7.每當圓環未完成結束觸摸的時候,都要保存當前每個layer的strokeStart和strokeEnd,不要忘記背景圓環

    override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
        
        for oneProgessLayer in layerArray {
            oneProgessLayer.strokeStart = oneProgessLayer.presentationLayer().strokeStart
            oneProgessLayer.strokeEnd = oneProgessLayer.presentationLayer().strokeEnd
            oneProgessLayer.removeAllAnimations()
        }
        
        backgroundLayer.strokeStart = backgroundLayer.presentationLayer().strokeStart
        backgroundLayer.removeAllAnimations()

    }

8.完成的標誌就是背景圓環整個被清除掉,所以只有背景圓環才設置了delegate

   override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
        if flag == true{
            circleComplete = true
            
            for oneProgessLayer in layerArray {
                oneProgessLayer.strokeStart = oneProgessLayer.presentationLayer().strokeStart
                oneProgessLayer.strokeEnd = oneProgessLayer.presentationLayer().strokeEnd
                oneProgessLayer.removeAllAnimations()
            }
            
            backgroundLayer.strokeEnd = backgroundLayer.presentationLayer().strokeEnd
            backgroundLayer.strokeStart = backgroundLayer.presentationLayer().strokeEnd
            backgroundLayer.removeAllAnimations()
        }
    }


測試:隨便找個controller添加即可

        let progessView = CustomProgessView(frame: CGRectMake(CGRectGetMidX(view.bounds) - 150, 100, 300, 300))
        view.addSubview(progessView)


結束。

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