iOS動畫系列之八:使用CAShapeLayer繪畫動態流量圖

非典型技術宅發表於2017-11-07

這篇文章通過使用CAShapeLayer和UIBezierPath來畫出一個動態顯示剩餘流量的小動畫。

最終實現的效果如下:

Paste_Image.png
Paste_Image.png

動態效果圖:

shapeLayerAni.gif
shapeLayerAni.gif

1. CAShapeLayer

實際中,能夠用CALayer完成的任務是比較少的,如果使用這個基礎圖層就能實現絕大部分的功能,我們們就沒有必要再開啟一個CAShapeLayer了嘛。

1.1 CAShapeLayer的優點

那CAShapeLayer到底有啥子優點嘛!

  • CAShapeLayer作為繼承自CALayer的子類,當然可使用CALayer的所有屬性。也就是說,爹有的它都有了。
  • CAShapeLayer是一個通過向量圖形而不是點陣圖來繪製的圖層子類。指定諸如顏色和線寬等屬性,用path來定義想要繪製的圖形,最後CAShapeLayer就自動渲染出來了。也就是說,CAShapeLayer不需要像普通CALayer一樣建立一個寄宿圖形。而且是向量圖形噢!!所以無論有多大,都不會佔用太多的記憶體。
  • CAShapeLayer使用了硬體加速,繪製同一圖形會比用CoreGraphics快很多。

1.2 基本屬性

屬性名 作用
path 影像的繪製路徑,path不支援隱式動畫
fillColor 填充path的顏色,或無填充。預設為不透明黑色。
fillRule 填充path的規則。選項是非零和偶奇。預設為非零。
lineCap 線端點型別
lineDashPattern 線性模版
lineDashPhase 線型模版的起點
lineJoin 線連線型別
lineWidth 線寬
miterLimit 最大斜接長度。
strokeColor 描邊顏色
strokeStart 描邊的起點
strokeEnd 描邊的終點

1.3 屬性解讀

能看到這裡,說明您已經不是一個沒有任何基礎的小白了。所以特別基礎的屬性就沒必要解釋一遍了。下面只是一個不常用或者立即起來稍微費點勁的屬性。

  • lineDashPattern: 這是一個NSNumber的陣列,索引從1開始記,奇數位數值表示實線長度,偶數位數值表示空白長度。系統會按照數值自動重複設定虛線。
  • miterLimit:最大斜接長度。斜接長度指的是在兩條線交匯處和外交之間的距離。只有lineJoin屬性為kCALineJoinMiter時miterLimit才有效。邊角的角度越小,斜接長度就會越大。為了避免斜接長度過長,我們可以使用miterLimit屬性。如果斜接長度超過miterLimit的值,邊角會以lineJoin的“bevel”即kCALineJoinBevel型別來顯示
  • strokeStart & strokeEnd : 描邊的起始點位置。範圍為0~1.

1.3.1 lineDashPattern畫虛線

Paste_Image.png
Paste_Image.png

basicLayer.lineDashPattern = [5,2,10,7]複製程式碼

這句話的意思是說這個虛線由四部分組成:

  1. 第一段實線長度為5
  2. 畫完長度為5畫素的實線之後,空2畫素
  3. 空完2畫素之後,再畫10畫素的實線
  4. 畫完長度為10畫素的實線之後,空7畫素
    然後重複這個陣列中的數值,一直不停的繪畫。

1.3.2 strokeStart & strokeEnd

strokeStart它表示描線開始的地方佔總路徑的百分比。預設值是0。
strokeEnd表示繪製結束的地方站總路徑的百分比。預設值是1,如果小於等於strokeStart 則繪製不出任何內容。

手畫一張圖,解釋一下啥意思:

Paste_Image.png
Paste_Image.png

2. 實戰:繪製一個鏤空圖層動畫

做好後的效果如下:

Paste_Image.png
Paste_Image.png

    fileprivate func hollowLayer(){
//        建立空心的layer
        let hollowLayer = CAShapeLayer()
        hollowLayer.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)
        view.layer.addSublayer(hollowLayer)
        hollowLayer.position = view.center

//        最外面待圓角的方形path
        let squarePath = UIBezierPath.init(roundedRect: CGRect(x: 0, y: 0, width: 100, height: 100), cornerRadius: 5)
//        中間鏤空的圓形path
        let hollowPath = UIBezierPath.init(ovalIn: CGRect(x: 10, y: 10, width: 80, height: 80))

        squarePath.append(hollowPath)
        hollowLayer.path = squarePath.cgPath

        hollowLayer.fillColor = UIColor.lightGray.cgColor
//        設定路徑的填充模式為兩個圖形的非交集
        hollowLayer.fillRule = kCAFillRuleEvenOdd



//        建立進度layer
        let processSectorLayer = CAShapeLayer()
        view.layer.addSublayer(processSectorLayer)
        processSectorLayer.bounds = CGRect(x: 0, y: 0, width: 70, height: 70)
        processSectorLayer.position = view.center

//        進度的path
        let processSectorPath = UIBezierPath.init(arcCenter: CGPoint.init(x: 35, y: 35), radius: 17.5, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)


        processSectorLayer.path = processSectorPath.cgPath

        processSectorLayer.lineWidth = 35

//        進度的起點和結束位置,設定進度條修改這個值和結束數值就可以了
        processSectorLayer.strokeStart = 0.5
        processSectorLayer.strokeEnd = 0.75

        processSectorLayer.strokeColor = UIColor.lightGray.cgColor

        processSectorLayer.fillColor = UIColor.clear.cgColor
    }複製程式碼

3. 使用CAShapeLayer繪畫動態流量圖

有了上面對於CAShapeLayer 的基礎訓練,繪製一個動態的流量圖就不是什麼困難的事情了。
實現後的效果如下:

3.1 實現思路

1,建立一個view,用來展示進度圓環。
2,在進度的view上面新增一個layer,用來展示進度圓環底部灰色的圓環。
3,在灰色的圓環上面,新增一個layer,用來顯示實際的進度。
4,建立一個定時器,定時器用來更新時時進度。

3.2 程式碼實現

在文章裡面我們們只PO出來一些關鍵的程式碼,如果想檢視原始檔,可以自行下載原始碼哈。

3.2.1 懶載入進度圓環的shapeLayer

    //   進度條layer
    lazy var circleProgressLayer: CAShapeLayer = {

        let circleProgressLayer = CAShapeLayer()

        let circleBounds = CGRect(x: 0, y: 0, width: 250, height: 250)
        circleProgressLayer.bounds = circleBounds
        circleProgressLayer.position = CGPoint(x: circleBounds.width / 2, y: circleBounds.height / 2)


        let circleProgressPath = UIBezierPath.init(arcCenter: CGPoint(x: circleBounds.width / 2, y: circleBounds.height / 2), radius: circleBounds.height / 2, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)

        circleProgressLayer.strokeStart = 0
        circleProgressLayer.strokeEnd = 1
        circleProgressLayer.path = circleProgressPath.cgPath
        circleProgressLayer.lineWidth = 10
        circleProgressLayer.strokeColor = UIColor.init(colorLiteralRed: 0, green: 151, blue: 255, alpha: 1).cgColor

        circleProgressLayer.fillColor = UIColor.clear.cgColor
        return circleProgressLayer
    }()複製程式碼

3.2.2 新增定時器

        //開啟定時器
        timer = Timer.scheduledTimer(timeInterval: 0.04, target: self, selector: #selector(progressShowNumber), userInfo: nil, repeats: true)複製程式碼

3.2.3 定時器的呼叫事件


// 定時器呼叫的方法
    @objc private func progressShowNumber(){

        if progressValue > expectValue - 1 && progressValue < expectValue {
            timer.invalidate()

            circleProgressLayer.strokeEnd = expectValue / 100
            progressLabel.text = "\(expectValue)%"
            return
        }
        if progressValue > expectValue {
            timer.invalidate()
            return
        }

//更新進度文字和進度條的strokeEnd
        circleProgressLayer.strokeEnd = CGFloat(progressValue) / 100
        progressLabel.text = "\(progressValue)%"

        progressValue += 1
    }複製程式碼

這兩天人正好在San Francisco,抽空去San Jose蘋果的大本營溜達溜達。到時候給大家放照哈。
原始碼可以在這裡下載。git.oschina.net/atypical/mu…

iOS動畫系列之CAShapeLayer(Swift版)

-----------------------華麗分割線,iOS動畫系列全集連結-------------------------------------------------
第一篇:iOS動畫系列之一:通過實戰學習CALayer和透視的原理。做一個帶時分秒指標的時鐘動畫(上)
第二篇:iOS動畫系列之二:通過實戰學習CALayer和透視的原理。做一個帶時分秒指標的時鐘動畫。包含了OC和Swift兩種原始碼(下)
第三篇:iOS動畫系列之三:Core Animation。介紹了Core Animation的常用屬性和方法。
第四篇:CABasic Animation。iOS動畫系列之四:基礎動畫之平移篇
第五篇:CABasic Animation。iOS動畫系列之五:基礎動畫之縮放篇&旋轉篇
第六篇:iOS動畫系列之六:利用CABasic Animation完成帶動畫特效的登入介面
第七篇:iOS動畫系列之七:實現類似Twitter的啟動動畫
第八篇:iOS動畫系列之八:使用CAShapeLayer繪畫動態流量圖
第九篇:iOS動畫系列之九:實現點讚的動畫及播放起伏指示器
第十篇:實戰系列:繪製雲霄飛車場景

相關文章