CoreAnimation實現(xiàn)CALayer自定義屬性動畫有幾種方式,在這里做了個Demo都實現(xiàn)了√裰現(xiàn)在拿出來研究下伶椿,順便根據(jù)蘋果的官方文檔做了個總結(jié)。
先上Demo的效果圖:
一、簡單的自定義屬性動畫
就是自定義的屬性脊另,使屬性像CALayer基類的屬性一樣导狡,可以通過設(shè)置屬性值來實現(xiàn)動畫效果。比如我demo中的白色圓環(huán)內(nèi)徑變化偎痛,設(shè)置圓環(huán)的內(nèi)徑增加10旱捧,然后反轉(zhuǎn),整個動畫效果無限循環(huán):
let innerCirqueAnim = CABasicAnimation.init()
innerCirqueAnim.keyPath = "circqueInnerRadius"
innerCirqueAnim.duration = 1
innerCirqueAnim.fromValue = animatorLayer!.circqueInnerRadius
innerCirqueAnim.toValue = animatorLayer!.circqueInnerRadius + 10
innerCirqueAnim.autoreverses = true
innerCirqueAnim.repeatCount = Float.greatestFiniteMagnitude
animatorLayer!.add(innerCirqueAnim, forKey: nil)
要實現(xiàn)這樣的效果需要的步驟如下:
1.創(chuàng)建一個基類繼承自CALayer
class ZoomAnimatingLayer: CALayer
2.創(chuàng)建與動畫效果有關(guān)的動態(tài)屬性( @NSManaged 等同于OC的Dynamic屬性)
// 圓環(huán)內(nèi)部半徑
@NSManaged public var circqueInnerRadius :CGFloat
// 圓環(huán)外部半徑
@NSManaged public var circqueOuterRadius :CGFloat
// 圓環(huán)中心
@NSManaged public var center :CGPoint
// 填充顏色
@NSManaged public var fillColor :UIColor?
- 注冊需動態(tài)重繪的屬性
// 注冊需動態(tài)重繪的屬性
override class func needsDisplay(forKey key: String) -> Bool{
print("needsDisplay(forKey key: String)")
if key == "circqueInnerRadius" || key == "circqueOuterRadius" || key == "pausingRectEdge" {
return true
}
return super.needsDisplay(forKey: key)
}
4.重寫draw(in ctx: CGContext)方法,在方法中繪制自定義屬性依賴的圖形(關(guān)于CoreGraphics內(nèi)容本文不贅述,此處繪制的是一個圓環(huán)绊袋,里面有一個方形)
override func draw(in ctx: CGContext) {
//繪圖相關(guān)內(nèi)容芽世,具體方法說明省略
var finalCenter = CGPoint.init(x: self.bounds.width/2, y: self.bounds.height/2)
var finalColor = UIColor.white
if !center.equalTo(CGPoint.zero) {
finalCenter = center
}
if fillColor != nil {
finalColor = fillColor!
}
let cirquePath = UIBezierPath.init()
cirquePath.addArc(withCenter: finalCenter, radius: circqueInnerRadius, startAngle: 0, endAngle: CGFloat.pi*2, clockwise: true)
cirquePath.addArc(withCenter: finalCenter, radius: circqueOuterRadius, startAngle: 0, endAngle: CGFloat.pi*2, clockwise: true)
ctx.addPath(cirquePath.cgPath)
ctx.setFillColor(finalColor.cgColor)
ctx.fillPath(using: CGPathFillRule.evenOdd)
if showPausing {
let roundRect = UIBezierPath.init(roundedRect: CGRect.init(x: finalCenter.x - pausingRectEdge/2, y: finalCenter.y - pausingRectEdge/2, width: pausingRectEdge, height: pausingRectEdge), cornerRadius: 5)
ctx.addPath(roundRect.cgPath)
ctx.setFillColor(finalColor.cgColor)
ctx.fillPath(using: CGPathFillRule.winding)
}
}
完成上面的設(shè)置之后,執(zhí)行本節(jié)最開始的設(shè)置的 animatorLayer!.add(innerCirqueAnim, forKey: nil)遂跟,簡單的自定義屬性動畫就可以正常的運行了。
二、CALayer屬性關(guān)聯(lián)動畫(Animating properties )
屬性關(guān)聯(lián)動畫是本人自己的定義卢肃,具體的效果是,在設(shè)置CALayer及其子類的屬性的時候(可以是基類屬性星压,也可以是自定義的屬性)践剂,同時執(zhí)行一個動畫效果(整個效果可以和屬性沒有關(guān)系)。
例如娜膘,我可以在設(shè)置方形的邊長的時候逊脯,改變CALayer的透明度;當(dāng)然竣贪,通常情況下军洼,是設(shè)置針對本屬性的動畫效果,例如演怎,在我設(shè)置方形的邊長的時候匕争,設(shè)置一個改變邊長尺寸的動畫;
//設(shè)置層的方形的邊長增加10
animatorLayer?.pausingRectEdge += 10
實現(xiàn)這種效果有兩種方案:
方案1:重寫CALayer的action(forKey event: String) -> CAAction? 方法爷耀,設(shè)置修改屬性時甘桑,增加動畫效果:
//設(shè)置屬性的屬性設(shè)置動畫:當(dāng)我們寫入該屬性的時候,會增加動畫效果歹叮;
override func action(forKey event: String) -> CAAction? {
print("action(forKey event: String)")
if self.presentation() != nil {
// self.presentation() 是在動畫過程中CALayer創(chuàng)建的針對動畫當(dāng)前顯示界面的顯示層跑杭;
print(self.presentation()!)
if event == "pausingRectEdge" {
let rectEdge = CABasicAnimation.init(keyPath: event)
rectEdge.fromValue = pausingRectEdge
rectEdge.duration = 2
//屬性設(shè)置動畫可以不是針對更改的屬性,可以是其他的屬性
// let opacityAni = CABasicAnimation.init(keyPath: "opacity")
// opacityAni.fromValue = 1
// opacityAni.fromValue = 0.5
// opacityAni.duration = 0.5
return rectEdge
}
}
return super.action(forKey: event)
}
方案2:設(shè)置CALayer的屬性actions,actions是一個Dictionary咆耿,其成員必須符合[String : CAAction]德谅;
//使用actions也能夠給屬性添加屬性設(shè)置動畫
let outterCirqueAnim = CABasicAnimation.init()
outterCirqueAnim.keyPath = "circqueOuterRadius"
outterCirqueAnim.duration = 3
outterCirqueAnim.fromValue = animatorLayer!.circqueOuterRadius
animatorLayer!.actions = NSMutableDictionary.init(object: outterCirqueAnim, forKey:"circqueOuterRadius" as NSCopying) as? [String : CAAction]
(animatorLayer?.actions!["circqueOuterRadius"] as! CABasicAnimation).fromValue = animatorLayer!.circqueOuterRadius
animatorLayer!.circqueOuterRadius += 10
可以看出,兩種效果其實都是給屬性增加了一個CAAction萨螺。第2種方法有一個好處窄做,可以不重新創(chuàng)建新的CALayer子類愧驱,直接對CALayer基類屬性增加動畫。但是如果動畫要多次執(zhí)行椭盏,屬性值需要手工修正组砚;第1種方法需要重新創(chuàng)建自定義的CALayer,但是屬性值不需要手工修正庸汗,而且針對自定義屬性方便惫确;
三、官方文檔
根據(jù)官方文檔蚯舱,如果想要定義CALayer的屬性動畫需要以下幾步:
1.定義一個遵循了CAAction協(xié)議的對象(所有的CAAnimation改化、CATransition等動畫相關(guān)的都遵從該協(xié)議);
2.將CAAction添加到CALayer上枉昏。方式有幾種:
實現(xiàn)[actionForLayer:forKey:]代理方法陈肛;(本文使用的方式)
添加到actions字典中;(本文使用的方式)
添加到style字典中(本文使用的方式)
3.觸發(fā)CALayer的屬性兄裂,蘋果提出有四條觸發(fā)方式(設(shè)置屬性的方式):設(shè)置屬性時自動觸發(fā)(本文使用的方式句旱,例如設(shè)置圓環(huán)內(nèi)徑);CALayer實例對象剛開始可見時自動觸發(fā)[設(shè)置kCAOnOrderIn屬性]晰奖;CALayer開始不可見時自動觸發(fā)(設(shè)置kCAOnOrderOut屬性)谈撒;添加到了一個CATransition(添加CATransition方法);