iOS核心动画高级技巧(笔记)——(六)隐式动画

动画是Core Animation库一个非常显著的特性。这一章我们来看看它是怎么做到的。具体来说,我们先来讨论框架自动完成的隐式动画(除非你明确禁用了这个功能)。

事务

  • 事务是Core Animation用来包含一系列属性动画集合的机制,任何用指定事务去改变可以做动画的图层属性都不会立刻发生变化,而是当事务一旦提交的时候开始用一个动画过渡到新值。
    事务是通过CATransaction类来做管理,可以用+begin+commit分别来入栈或者出栈。CATransaction+begin+commit方法与UIView+beginAnimations:context:+commitAnimations方法类似。
    任何可以做动画的图层属性都会被添加到栈顶的事务,你可以通过+setAnimationDuration:方法设置当前事务的动画时间,或者通过+animationDuration方法来获取值(默认0.25秒)。

完成块

  • 基于UIView的block的动画允许你在动画结束的时候提供一个完成的动作。CATranscation接口提供的+setCompletionBlock:方法也有同样的功能。来看一个例子,首先创建一个蓝色的方块,然后添加一个按钮,随机改变它的颜色,再添加一个完成之后的block,用来在每次颜色变化结束之后切换到另一个旋转45度的动画。点击按钮,你会发现图层的颜色平滑过渡到一个新值,而不是跳变。并且旋转动画要比颜色渐变快得多,这是因为完成块是在颜色渐变的事务提交并出栈之后才被执行,于是,用默认的事务做变换,默认的时间也就变成了0.25秒。
    var colorLayer = CALayer()
    @IBOutlet weak var layerView: UIView!
    @IBAction func changeColor(sender: UIButton) {
    CATransaction.begin()
    CATransaction.setAnimationDuration(2.0)

      let red = CGFloat(arc4random()) / CGFloat(INT_MAX)
      let green = CGFloat(arc4random()) / CGFloat(INT_MAX)
      let blue = CGFloat(arc4random()) / CGFloat(INT_MAX)
      self.colorLayer.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0).CGColor
      
      CATransaction.setCompletionBlock({
          var transform = self.colorLayer.affineTransform()
          transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2) / 2.0)
          self.colorLayer.setAffineTransform(transform)
          
      })
      
      CATransaction.commit()
    }
    override func viewDidLoad() {
      self.colorLayer.frame = CGRectMake(50, 50, 100, 100)
      self.colorLayer.backgroundColor = UIColor.blueColor().CGColor
      self.layerView.layer.addSublayer(self.colorLayer)
    }
    
gif

图层行为

  • 对上一个例子做一点修改,移除colorLayer,并且直接设置layerView关联图层的背景色,现在是直接对UIView关联的图层做动画而不是一个单独的图层。运行程序,会发现当按下按钮,图层颜色瞬间切换到新的值,而不是之前平滑过渡的动画。这是因为隐式动画被UIVew关联图层给禁用了。
    UIView关联的图层禁用了隐式动画,对这种图层做动画的唯一办法就是使用UIView的动画函数(而不是依赖CATransaction),或者继承UIView,并覆盖-actionForLayer:forKey:方法,或者直接创建一个显式动画(具体细节见第七章)。对于单独存在的图层,我们可以通过实现图层的-actionForLayer:forKey:委托方法,或者提供一个actions字典来控制隐式动画。
    CATransacition有个方法叫做+setDisableActions:,可以用来对所有属性打开或者关闭隐式动画。
    我们来对颜色渐变的例子使用一个不同的行为,通过给colorLayer设置一个自定义的actions字典。 行为通常是一个被Core Animation隐式调用的显式动画对象。这里我们使用的是一个实现了CATransaction的实例,叫做推进过渡。第七章中将会详细解释过渡,不过对于现在,知道CATransition响应CAAction协议,并且可以当做一个图层行为就足够了。结果是不论在什么时候改变背景颜色,新的色块都是从左侧滑入,而不是默认的渐变效果。
    @IBOutlet weak var layerView: UIView!
    var colorLayer = CALayer()

    @IBAction func changeColor(sender: UIButton) {
      CATransaction.begin()
      CATransaction.setAnimationDuration(2)
      
      let red = CGFloat(arc4random() / UINT32_MAX)
      let green = CGFloat(arc4random()) / CGFloat(INT_MAX)
      let blue = CGFloat(arc4random()) / CGFloat(INT_MAX)
      self.colorLayer.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0).CGColor
      
      CATransaction.commit()
    }
    override func viewDidLoad() {
      self.colorLayer.frame = CGRectMake(50, 50, 100, 100)
      self.colorLayer.backgroundColor = UIColor.blueColor().CGColor
      self.layerView.layer.addSublayer(self.colorLayer)
      
      let transition = CATransition()
      transition.type = kCATransitionPush
      transition.subtype = kCATransitionFromLeft
      self.colorLayer.actions = ["backgroundColor" : transition] 
    }
    
推进过渡

呈现与模型

  • 在iOS中,屏幕每秒钟重绘60次。如果动画时长比60分之一秒要长,Core Animation就需要在设置一次新值和新值生效之间,对屏幕上的图层进行重新组织。这意味着CALayer除了“真实”值(就是你设置的值)之外,必须要知道当前显示在屏幕上的属性值的记录。
    每个图层属性的显示值都被存储在一个叫做呈现图层的独立图层当中,他可以通过-presentationLayer方法来访问。这个呈现图层实际上是模型图层的复制,但是它的属性值代表了在任何指定时刻当前外观效果。换句话说,你可以通过呈现图层的值来获取当前屏幕上真正显示出来的值。
    一个移动的图层是如何通过数据模型呈现的

    大多数情况下,你不需要直接访问呈现图层,你可以通过和模型图层的交互,来让Core Animation更新显示。两种情况下呈现图层会变得很有用,一个是同步动画,一个是处理用户交互。
    1.如果你在实现一个基于定时器的动画(见第10章“基于定时器的动画”),而不仅仅是基于事务的动画,这个时候准确地知道在某一时刻图层显示在什么位置就会对正确摆放图层很有用了。
    2.如果你想让你做动画的图层响应用户输入,你可以使用-hitTest:方法来判断指定图层是否被触摸,这时候对呈现图层而不是模型图层调用-hitTest:会显得更有意义,因为呈现图层代表了用户当前看到的图层位置,而不是当前动画结束之后的位置
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let point = (touches as NSSet).anyObject()?.locationInView(self.view)
    if ((self.colorLayer.presentationLayer()!.hitTest(point!)) != nil) {
    }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容