CoreAnimation实现CALayer自定义属性动画有几种方式,在这里做了个Demo都实现了。现在拿出来研究下,顺便根据苹果的官方文档做了个总结。
先上Demo的效果图:
一、简单的自定义属性动画
就是自定义的属性,使属性像CALayer基类的属性一样,可以通过设置属性值来实现动画效果。比如我demo中的白色圆环内径变化,设置圆环的内径增加10,然后反转,整个动画效果无限循环:
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)
要实现这样的效果需要的步骤如下:
1.创建一个基类继承自CALayer
class ZoomAnimatingLayer: CALayer
2.创建与动画效果有关的动态属性( @NSManaged 等同于OC的Dynamic属性)
// 圆环内部半径
@NSManaged public var circqueInnerRadius :CGFloat
// 圆环外部半径
@NSManaged public var circqueOuterRadius :CGFloat
// 圆环中心
@NSManaged public var center :CGPoint
// 填充颜色
@NSManaged public var fillColor :UIColor?
- 注册需动态重绘的属性
// 注册需动态重绘的属性
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)方法,在方法中绘制自定义属性依赖的图形(关于CoreGraphics内容本文不赘述,此处绘制的是一个圆环,里面有一个方形)
override func draw(in ctx: CGContext) {
//绘图相关内容,具体方法说明省略
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)
}
}
完成上面的设置之后,执行本节最开始的设置的 animatorLayer!.add(innerCirqueAnim, forKey: nil),简单的自定义属性动画就可以正常的运行了。
二、CALayer属性关联动画(Animating properties )
属性关联动画是本人自己的定义,具体的效果是,在设置CALayer及其子类的属性的时候(可以是基类属性,也可以是自定义的属性),同时执行一个动画效果(整个效果可以和属性没有关系)。
例如,我可以在设置方形的边长的时候,改变CALayer的透明度;当然,通常情况下,是设置针对本属性的动画效果,例如,在我设置方形的边长的时候,设置一个改变边长尺寸的动画;
//设置层的方形的边长增加10
animatorLayer?.pausingRectEdge += 10
实现这种效果有两种方案:
方案1:重写CALayer的action(forKey event: String) -> CAAction? 方法,设置修改属性时,增加动画效果:
//设置属性的属性设置动画:当我们写入该属性的时候,会增加动画效果;
override func action(forKey event: String) -> CAAction? {
print("action(forKey event: String)")
if self.presentation() != nil {
// self.presentation() 是在动画过程中CALayer创建的针对动画当前显示界面的显示层;
print(self.presentation()!)
if event == "pausingRectEdge" {
let rectEdge = CABasicAnimation.init(keyPath: event)
rectEdge.fromValue = pausingRectEdge
rectEdge.duration = 2
//属性设置动画可以不是针对更改的属性,可以是其他的属性
// 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:设置CALayer的属性actions,actions是一个Dictionary,其成员必须符合[String : CAAction];
//使用actions也能够给属性添加属性设置动画
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种方法有一个好处,可以不重新创建新的CALayer子类,直接对CALayer基类属性增加动画。但是如果动画要多次执行,属性值需要手工修正;第1种方法需要重新创建自定义的CALayer,但是属性值不需要手工修正,而且针对自定义属性方便;
三、官方文档
根据官方文档,如果想要定义CALayer的属性动画需要以下几步:
1.定义一个遵循了CAAction协议的对象(所有的CAAnimation、CATransition等动画相关的都遵从该协议);
2.将CAAction添加到CALayer上。方式有几种:
实现[actionForLayer:forKey:]代理方法;(本文使用的方式)
添加到actions字典中;(本文使用的方式)
添加到style字典中(本文使用的方式)
3.触发CALayer的属性,苹果提出有四条触发方式(设置属性的方式):设置属性时自动触发(本文使用的方式,例如设置圆环内径);CALayer实例对象刚开始可见时自动触发[设置kCAOnOrderIn属性];CALayer开始不可见时自动触发(设置kCAOnOrderOut属性);添加到了一个CATransition(添加CATransition方法);
具体Demo见我的GitHub