iOS CoreAnimation/CALayer 自定义属性动画demo及总结

CoreAnimation实现CALayer自定义属性动画有几种方式,在这里做了个Demo都实现了。现在拿出来研究下,顺便根据苹果的官方文档做了个总结。

先上Demo的效果图:


自定义属性动画效果图.gif

一、简单的自定义属性动画
就是自定义的属性,使属性像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?
    
  1. 注册需动态重绘的属性
 // 注册需动态重绘的属性
     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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,463评论 6 30
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,094评论 5 13
  • 书写的很好,翻译的也棒!感谢译者,感谢感谢! iOS-Core-Animation-Advanced-Techni...
    钱嘘嘘阅读 2,289评论 0 6
  • 如果想让事情变得顺利,只有靠自己 -- 夏尔·纪尧姆 上一章介绍了隐式动画的概念。隐式动画是在iOS平台创建动态用...
    雪_晟阅读 564评论 0 1
  • 转载:http://www.cnblogs.com/jingdizhiwa/p/5601240.html 1.ge...
    F麦子阅读 1,530评论 0 1