這篇文章經過兩個加載動畫向你們介紹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因爲更底層、動畫屬性更多,因此能夠實現出更靈活、更豐富的動畫。動畫
Layer動畫系列的文章,我不許備系統的從簡單到複雜的知識進行講解,我會經過各類實戰示例,示例中用到什麼知識點就講什麼知識點。spa
第一個動畫讓咱們來實現Google Chrome瀏覽器加載時頁簽上的等待動畫:.net
新建項目GoogleLoading,打開Main.storyboard
,拖拽一個UIView到ViewController中,添加好約束,自行設置ViewController和UIView的背景色,這裏UIView的背景色我設置爲無色:
而後添加該View的Outlet到ViewController
中,這個UIView就是要展現加載動畫的View:
[cpp] view plaincopy
@IBOutlet weak var loadingView: UIView!
打開ViewController.swift
,申明一個常量屬性ovalShapleLayer
:
[cpp] view plaincopy
let ovalShapeLayer: CAShapeLayer = CAShapeLayer()
ovalShapleLayer
的類型是CAShapleLayer
,它是CALayer
的爲數很少的子類之一。它的做用是在屏幕上畫出各類形狀,不管是簡單的圓形、方形仍是複雜的五角星或不規則圖形都難不住它。CAShapeLayer
有以下一些主要屬性:
strokeColor:筆畫顏色。
strokeStart:筆畫開始位置。
strokeEnd:筆畫結束位置。
fillColor:圖形填充顏色。
lineWidth:筆畫寬度,即筆畫的粗細程度。
lineDashPattern:虛線模式。
path:圖形的路徑。
lineCap:筆畫未閉合位置的形狀。
咱們之所要申明一個CAShapeLayer
,是由於要用它在屏幕上畫出一個圓形。下面在viewDidLoad()
方法中添加以下代碼:
[cpp] view plaincopy
ovalShapeLayer.strokeColor = UIColor.whiteColor().CGColor
ovalShapeLayer.fillColor = UIColor.clearColor().CGColor
ovalShapeLayer.lineWidth = 7
這幾個屬性剛纔已經向你們介紹過了,這三行代碼的意思是咱們畫出的圓形筆畫顏色是白色,沒有填充色,筆畫的寬度爲7。接着咱們申明這個圓形的半徑,使這個圓形的大小爲容納它視圖大小的80%:
[cpp] view plaincopy
let ovalRadius = loadingView.frame.size.height/2 * 0.8
最後咱們設置ovalShapeLayer
的路徑,這是最關鍵的一步,由於你要告知CAShapeLayer
按照什麼路徑繪製圖形,讓咱們接着添加以下代碼:
[cpp] view plaincopy
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
loadingView.layer.addSublayer(ovalShapeLayer)
編譯運行看看效果:
完美的一個圓形。接下來咱們要作的是讓這個圓只顯示一部分,由於Google的加載動畫只有大概五分之二的圓形輪廓。讓咱們繼續將目光集中在viewDidLoad()
方法中,在loadingView.layer.addSublayer(ovalShapeLayer)
這行代碼上面添加另外一行代碼:
[cpp] view plaincopy
ovalShapeLayer.strokeEnd = 0.4
上面的代碼將ovalShapeLayer
的strokeEnd
屬性設置爲0.4,意思是ovalShapeLayer
在繪製圓形時只畫整個圓形的五分之二,即筆畫結束的位置在整個圓形輪廓的五分之二處。編譯運行看看效果:
看來是咱們想要的效果,可是仍有一處細節須要咱們完善,看看Google的加載動畫,藍色的部分圓形輪廓兩頭是圓形的,而咱們的圓形輪廓兩頭是方形的。這個問題很好解決,仍然在loadingView.layer.addSublayer(ovalShapeLayer)
這行代碼上面添加一行代碼:
[cpp] view plaincopy
ovalShapeLayer.lineCap = kCALineCapRound
這行代碼的意思是將筆畫兩頭的形狀設置爲圓形,對應的還有兩個常量kCALineCapButt
,kCALineCapSquare
,你們能夠試試。再次編譯運行看看效果:
到目前爲止,咱們經過CALayer繪製出了動畫的主體,接下來要讓它動起來。在ViewController.swift
中添加beginSimpleAnimate()
方法:
[cpp] view plaincopy
func beginSimpleAnimation() {
let rotate = CABasicAnimation(keyPath: "transform.rotation")
rotate.duration = 1.5
rotate.fromValue = 0
rotate.toValue = 2 * M_PI
rotate.repeatCount = HUGE
loadingView.layer.addAnimation(rotate, forKey: nil)
}
在這個方法中,咱們又看到了新面孔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
override func viewWillAppear(animated: Bool) {
beginSimpleAnimation()
}
編譯運行看看效果:
至此咱們的第一個簡單的CALayer動畫就完成了,在下一節咱們一塊兒實現一個更加有意思的加載動畫,從而向你們介紹新的動畫類型及動畫組合。
讓咱們先看看要實現的效果:
這種加載動畫在不少應用中都出現過,好比網易新聞、Win版的谷歌瀏覽器中都有使用。下面就讓咱們一步一步來實現吧,首先打開Main.storyboard
,新添加一個UIView,在ViewController.swift
中添加Outlet:
而後定義一個新的CAShapeLayer:
[cpp] view plaincopy
let anotherOvalShapeLayer: CAShapeLayer = CAShapeLayer()
在viewDidLoad()
方法中對它進行設置,並將其添加到剛纔建立的complexLoadingView
中:
[cpp] view plaincopy
anotherOvalShapeLayer.strokeColor = UIColor.whiteColor().CGColor
anotherOvalShapeLayer.fillColor = UIColor.clearColor().CGColor
anotherOvalShapeLayer.lineWidth = 7
let anotherOvalRadius = complexLoadingView.frame.size.height/2 * 0.8
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
anotherOvalShapeLayer.lineCap = kCALineCapRound
complexLoadingView.layer.addSublayer(anotherOvalShapeLayer)
這些操做在上一個動畫都已經作過一遍了,這裏就再也不解釋。編譯運行看看是否屏幕上又出現了一個圓圈呢:
接下來在ViewController.swift
中添加一個方法beginComplexAnimation()
:
[cpp] view plaincopy
let strokeStartAnimate = CABasicAnimation(keyPath: "strokeStart")
strokeStartAnimate.fromValue = -0.5
strokeStartAnimate.toValue = 1
let strokeEndAnimate = CABasicAnimation(keyPath: "strokeEnd")
strokeEndAnimate.fromValue = 0.0
strokeEndAnimate.toValue = 1
let strokeAnimateGroup = CAAnimationGroup()
strokeAnimateGroup.duration = 1.5
strokeAnimateGroup.repeatCount = HUGE
strokeAnimateGroup.animations = [strokeStartAnimate, strokeEndAnimate]
anotherOvalShapeLayer.addAnimation(strokeAnimateGroup, forKey: nil)
這裏出現了兩個新的動畫類型,筆畫開始動畫和筆畫結束動畫,咱們雖然使用CAShapeLayer繪製了一個圓圈,可是它也存在筆畫起始位置和筆畫終止位置,只不過它倆在同一個位置而已,筆畫動畫的位置取值在0–1之間,0表明繪製路徑的起始位置,1表明繪製路徑的終止位置。
因此strokeStartAnimate
動畫讓繪製圓圈的筆畫起始位置從–0.5開始,目的是讓筆畫起始繪製時等待一段時間,也就是起始位置延遲繪製。而strokeEndAnimate
動畫讓繪製圓圈的筆畫終止位置正常的從0繪製到1。這樣一來筆畫兩頭繪製的時間就會不同,會有一個時間差,這樣就有圓圈不斷繪製又不斷被擦除的效果。
strokeStartAnimate
和strokeEndAnimate
是兩個動畫,如何做用於一個Layer上呢?這時就要用到CAAnimationGroup
,顧名思義它是將多個動畫組成一個組,在一個動畫組裏,子動畫會同時進行。動畫組能夠設置動畫持續時間、重複次數以及子動畫數組。最後將動畫組加在Layer上便可。
最後在viewWillAppear()
方法中調用beginComplexAnimation()
方法:
[cpp] view plaincopy
override func viewWillAppear(animated: Bool) {
beginSimpleAnimation()
beginComplexAnimation()
}
編譯運行看看效果:
CALayer動畫能夠實現比UIView動畫更豐富、更底層、效率更高的動畫。可是在實際的應用開發中,咱們應該按需所用,能用UIView動畫實現的咱們就能夠不用CALayer動畫,它倆沒有誰優誰劣之分。這篇文章只是CALayer動畫的引子,讓你們對CALayer動畫有初步的瞭解和認識,以後我在文章中會經過更多的實例幫你們更深刻的認識CALayer動畫,從而提高本身應用的用戶體驗。