CALayer Animation實踐(一):讓應用靈動起來!

這篇文章經過兩個加載動畫向你們介紹CALayer的動畫。按照面向對象的思想說,Layer其實就是一個模型類,像Java裏的POJO類同樣,它包含若干屬性,並無任何處理邏輯的方法,這些屬性影響着顯示在Layer中的內容。咱們先來看看UIView和CALayer之間有什麼區別和聯繫。swift

  • 聯繫:Layer是View背後的那個女人。每個UIView後面都有對應的CALayer,你們看到的在UIView中顯示的內容實際上是在CALayer中。數組

  • 區別:瀏覽器

    • View有複雜的、各類組合的佈局機制。Layer只有極簡單的佈局。app

    • View能夠響應用戶交互。Layer不能響應用戶交互。框架

    • View中的繪畫邏輯有CPU執行。Layer中的繪畫直接有GPU執行。ide

    • View有豐富的、功能強大的子類。Layer只有不多的幾個子類。佈局

    • View動畫屬性較少,侷限性較大。Layer因爲更底層、動畫屬性更多,因此能夠實現出更靈活、更豐富的動畫。動畫

第一個CALayer動畫

Layer動畫系列的文章,我不許備系統的從簡單到複雜的知識進行講解,我會經過各類實戰示例,示例中用到什麼知識點就講什麼知識點。spa

第一個動畫讓咱們來實現Google Chrome瀏覽器加載時頁簽上的等待動畫:.net

新建項目GoogleLoading,打開Main.storyboard,拖拽一個UIView到ViewController中,添加好約束,自行設置ViewController和UIView的背景色,這裏UIView的背景色我設置爲無色:

而後添加該View的Outlet到ViewController中,這個UIView就是要展現加載動畫的View:

[cpp] view plaincopy

  1. @IBOutlet weak var loadingView: UIView!  

打開ViewController.swift,申明一個常量屬性ovalShapleLayer

[cpp] view plaincopy

  1. let ovalShapeLayer: CAShapeLayer = CAShapeLayer()  

ovalShapleLayer的類型是CAShapleLayer,它是CALayer的爲數很少的子類之一。它的做用是在屏幕上畫出各類形狀,不管是簡單的圓形、方形仍是複雜的五角星或不規則圖形都難不住它。CAShapeLayer有以下一些主要屬性:

  • strokeColor:筆畫顏色。

  • strokeStart:筆畫開始位置。

  • strokeEnd:筆畫結束位置。

  • fillColor:圖形填充顏色。

  • lineWidth:筆畫寬度,即筆畫的粗細程度。

  • lineDashPattern:虛線模式。

  • path:圖形的路徑。

  • lineCap:筆畫未閉合位置的形狀。

咱們之所要申明一個CAShapeLayer,是由於要用它在屏幕上畫出一個圓形。下面在viewDidLoad()方法中添加以下代碼:

[cpp] view plaincopy

  1. ovalShapeLayer.strokeColor = UIColor.whiteColor().CGColor  

  2. ovalShapeLayer.fillColor = UIColor.clearColor().CGColor  

  3. ovalShapeLayer.lineWidth = 7  

這幾個屬性剛纔已經向你們介紹過了,這三行代碼的意思是咱們畫出的圓形筆畫顏色是白色,沒有填充色,筆畫的寬度爲7。接着咱們申明這個圓形的半徑,使這個圓形的大小爲容納它視圖大小的80%:

[cpp] view plaincopy

  1. let ovalRadius = loadingView.frame.size.height/2 * 0.8  

最後咱們設置ovalShapeLayer的路徑,這是最關鍵的一步,由於你要告知CAShapeLayer按照什麼路徑繪製圖形,讓咱們接着添加以下代碼:

[cpp] view plaincopy

  1. ovalShapeLayer.path = UIBezierPath(ovalInRect: CGRect(x: loadingView.frame.size.width/2 - ovalRadius, y: loadingView.frame.size.height/2 - ovalRadius, width: ovalRadius * 2, height: ovalRadius * 2)).CGPath  

這裏出現了新面孔UIBezierPath,它能夠建立基於矢量的路徑,是Core Graphics框架關於path的封裝。UIBezierPath能夠定義簡單的形狀路徑,如橢圓、矩形,或者有多個直線和曲線段組成的形狀。在這裏咱們要使用它的初始化方法init(ovalInRect rect: CGRect)定義一個正圓的路徑。設置完路徑後,將ovalShapeLayer添加到loadingView視圖的Layer中,它就能夠按照設定好的路徑在loadingView中繪製圖形了:

[cpp] view plaincopy

  1. loadingView.layer.addSublayer(ovalShapeLayer)  

編譯運行看看效果:

完美的一個圓形。接下來咱們要作的是讓這個圓只顯示一部分,由於Google的加載動畫只有大概五分之二的圓形輪廓。讓咱們繼續將目光集中在viewDidLoad()方法中,在loadingView.layer.addSublayer(ovalShapeLayer)這行代碼上面添加另外一行代碼:

[cpp] view plaincopy

  1. ovalShapeLayer.strokeEnd = 0.4  

上面的代碼將ovalShapeLayerstrokeEnd屬性設置爲0.4,意思是ovalShapeLayer在繪製圓形時只畫整個圓形的五分之二,即筆畫結束的位置在整個圓形輪廓的五分之二處。編譯運行看看效果:

看來是咱們想要的效果,可是仍有一處細節須要咱們完善,看看Google的加載動畫,藍色的部分圓形輪廓兩頭是圓形的,而咱們的圓形輪廓兩頭是方形的。這個問題很好解決,仍然在loadingView.layer.addSublayer(ovalShapeLayer)這行代碼上面添加一行代碼:

[cpp] view plaincopy

  1. ovalShapeLayer.lineCap = kCALineCapRound  


這行代碼的意思是將筆畫兩頭的形狀設置爲圓形,對應的還有兩個常量kCALineCapButtkCALineCapSquare,你們能夠試試。再次編譯運行看看效果:

到目前爲止,咱們經過CALayer繪製出了動畫的主體,接下來要讓它動起來。在ViewController.swift中添加beginSimpleAnimate()方法:

[cpp] view plaincopy

  1. func beginSimpleAnimation() {  

  2.     let rotate = CABasicAnimation(keyPath: "transform.rotation")  

  3.     rotate.duration = 1.5  

  4.     rotate.fromValue = 0  

  5.     rotate.toValue = 2 * M_PI  

  6.     rotate.repeatCount = HUGE  

  7.     loadingView.layer.addAnimation(rotate, forKey: nil)  

  8. }  

在這個方法中,咱們又看到了新面孔CABasicAnimation,該類提供了基本的、單關鍵幀的Layer屬性動畫,經過animationWithKeyPath:初始化方法,根據keyPath建立不一樣的CAPropertyAnimation實例。經常使用的keyPath有以下一些:

  • transform.rotation:旋轉動畫。

  • transform.ratation.x:按x軸旋轉動畫。

  • transform.ratation.y:按y軸旋轉動畫。

  • transform.ratation.z:按z軸旋轉動畫。

  • transform.scale:按比例放大縮小動畫。

  • transform.scale.x:在x軸按比例放大縮小動畫。

  • transform.scale.y:在y軸按比例放大縮小動畫。

  • transform.scale.z:在z軸按比例放大縮小動畫。

  • position:移動位置動畫。

  • opacity:透明度動畫。

以上只是一部分經常使用的動畫keyPath,更多的但願你們在實際運用中去挖掘。在beginSimpleAnimation()方法中,咱們使用了transform.rotation,建立了一個旋轉動畫的實例,而後給該動畫設置了四個屬性:

  • duration:動畫持續時間。

  • fromValue:動畫起始值。

  • toValue:動畫結束值。

  • repeatCount:重複次數。

該方法設置這幾個屬性的含義爲使動畫主體不停的旋轉,旋轉一圈的時間爲1.5秒。以上這幾個概念在UIView的動畫中一樣存在,你們應該都已經比較熟悉了。而後使用Layer的addAnimation(anim: CAAnimation, forKey key: String?)方法將旋轉動畫實例添加到目標Layer中,該方法的key是用來標示添加的動畫,便於之後重複使用時能方便的檢索,若是沒有需求能夠傳值nil。最後viewWillAppear方法中調用beginSimpleAnimation()方法:

[cpp] view plaincopy

  1. override func viewWillAppear(animated: Bool) {  

  2.     beginSimpleAnimation()  

  3. }  

編譯運行看看效果:

至此咱們的第一個簡單的CALayer動畫就完成了,在下一節咱們一塊兒實現一個更加有意思的加載動畫,從而向你們介紹新的動畫類型及動畫組合。

Stroke Animation與Animation Group

讓咱們先看看要實現的效果:

這種加載動畫在不少應用中都出現過,好比網易新聞、Win版的谷歌瀏覽器中都有使用。下面就讓咱們一步一步來實現吧,首先打開Main.storyboard,新添加一個UIView,在ViewController.swift中添加Outlet:

而後定義一個新的CAShapeLayer:

[cpp] view plaincopy

  1. let anotherOvalShapeLayer: CAShapeLayer = CAShapeLayer()  

viewDidLoad()方法中對它進行設置,並將其添加到剛纔建立的complexLoadingView中:

[cpp] view plaincopy

  1. anotherOvalShapeLayer.strokeColor = UIColor.whiteColor().CGColor  

  2. anotherOvalShapeLayer.fillColor = UIColor.clearColor().CGColor  

  3. anotherOvalShapeLayer.lineWidth = 7  

  4.           

  5. let anotherOvalRadius = complexLoadingView.frame.size.height/2 * 0.8  

  6. anotherOvalShapeLayer.path = UIBezierPath(ovalInRect: CGRect(x: complexLoadingView.frame.size.width/2 - anotherOvalRadius, y: complexLoadingView.frame.size.height/2 - anotherOvalRadius, width: anotherOvalRadius * 2, height: anotherOvalRadius * 2)).CGPath  

  7. anotherOvalShapeLayer.lineCap = kCALineCapRound  

  8.           

  9. complexLoadingView.layer.addSublayer(anotherOvalShapeLayer)  

這些操做在上一個動畫都已經作過一遍了,這裏就再也不解釋。編譯運行看看是否屏幕上又出現了一個圓圈呢:

接下來在ViewController.swift中添加一個方法beginComplexAnimation()

[cpp] view plaincopy

  1. let strokeStartAnimate = CABasicAnimation(keyPath: "strokeStart")  

  2. strokeStartAnimate.fromValue = -0.5  

  3. strokeStartAnimate.toValue = 1  

  4.           

  5. let strokeEndAnimate = CABasicAnimation(keyPath: "strokeEnd")  

  6. strokeEndAnimate.fromValue = 0.0  

  7. strokeEndAnimate.toValue = 1  

  8.           

  9. let strokeAnimateGroup = CAAnimationGroup()  

  10. strokeAnimateGroup.duration = 1.5  

  11. strokeAnimateGroup.repeatCount = HUGE  

  12. strokeAnimateGroup.animations = [strokeStartAnimate, strokeEndAnimate]  

  13. anotherOvalShapeLayer.addAnimation(strokeAnimateGroup, forKey: nil)  

這裏出現了兩個新的動畫類型,筆畫開始動畫和筆畫結束動畫,咱們雖然使用CAShapeLayer繪製了一個圓圈,可是它也存在筆畫起始位置和筆畫終止位置,只不過它倆在同一個位置而已,筆畫動畫的位置取值在0–1之間,0表明繪製路徑的起始位置,1表明繪製路徑的終止位置。

因此strokeStartAnimate動畫讓繪製圓圈的筆畫起始位置從–0.5開始,目的是讓筆畫起始繪製時等待一段時間,也就是起始位置延遲繪製。而strokeEndAnimate動畫讓繪製圓圈的筆畫終止位置正常的從0繪製到1。這樣一來筆畫兩頭繪製的時間就會不同,會有一個時間差,這樣就有圓圈不斷繪製又不斷被擦除的效果。

strokeStartAnimatestrokeEndAnimate是兩個動畫,如何做用於一個Layer上呢?這時就要用到CAAnimationGroup,顧名思義它是將多個動畫組成一個組,在一個動畫組裏,子動畫會同時進行。動畫組能夠設置動畫持續時間、重複次數以及子動畫數組。最後將動畫組加在Layer上便可。

最後在viewWillAppear()方法中調用beginComplexAnimation()方法:

[cpp] view plaincopy

  1. override func viewWillAppear(animated: Bool) {  

  2.     beginSimpleAnimation()  

  3.     beginComplexAnimation()  

  4. }  

編譯運行看看效果:

總結

CALayer動畫能夠實現比UIView動畫更豐富、更底層、效率更高的動畫。可是在實際的應用開發中,咱們應該按需所用,能用UIView動畫實現的咱們就能夠不用CALayer動畫,它倆沒有誰優誰劣之分。這篇文章只是CALayer動畫的引子,讓你們對CALayer動畫有初步的瞭解和認識,以後我在文章中會經過更多的實例幫你們更深刻的認識CALayer動畫,從而提高本身應用的用戶體驗。

相關文章
相關標籤/搜索