Swift_ios_UIView动画,CA核心动画那些事(2)

秒学SWIFT

花了将近一周的时间去学习ios动画,因为对于一个ios开发者来说,动画内容绝对是一门必修课。听了不少课,也看了不少文章,终于对动画有了初步的了解和自己的一些小总结。但是傻傻笨笨的我,给自己挖了一个坑,为了填这个坑花了快两天时间,真够笨的!不过最终还是完美解决,小小成就感就来了!

关于动画,网上流传着许许多多的文章,基本上都适合初学者入门。那些文章大概思路都是这样的:
1.介绍什么是动画
2.动画可以分为UIView动画和CA动画。(其他动画暂时忽略)
3.UIView动画分为常规模式和闭包模式。现在主要用闭包模式。
4.UIView闭包模式有基本“杜蕾斯”动画,杜蕾斯弹性动画,转场动画,关键帧动画。
5.CA动画有基本动画,转场动画,关键帧动画,组动画,弹性动画。
6.UIView动画和CA动画的关系,即UIView动画是CA动画的封装。各有优势各有特色。

无可否认的是这些对自己在初步认识动画阶段,起到了很大的帮助作用,起码让自己对动画有个大概的了解。但是仅仅这些,还是会让初学者掉进坑里,比如我。所以学习动画以后的总结经验,就不可或缺了。这才不会让自己第二次掉进同一个坑。待会我要记录下自己怎么掉坑,填坑的。

我是先学习CA动画的,明白了CA动画能够细微调整的意义,也见识了CA动画是怎么让开发者去掌控每一个环节的。这个过程中我也笔记了基础的知识:(有一部分摘抄,一部分是自己总结)

//CAAnimation:
//所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,应该使用它具体的子类
//duration:动画的持续时间
//repeatCount:动画的重复次数
//repeatDuration:动画的重复时间
//removedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards
//fillMode:决定当前对象在非active时间段的行为.比如动画开始之前,动画结束之后
//beginTime:可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间
//timingFunction:速度控制函数,控制动画运行的节奏
//delegate:动画代理
//keyPath: 通过指定CALayer的一个属性名称达到相应的动画效果,比如说,指定"position"为keyPath,就修改CALayer的position属性值,以达到平移的动画效果


//A. CGAffineTransform
//从CG就可以看出它是属于Core Graphics的东西,实际上UIView的transform属性就是CGAffineTransform类型,用它可以做二维平面上的缩放、旋转、平移。
//B.CATransform3D(layer)
//它可以做到让图层在三维空间内平移、旋转等。

  • CABasicAnimation动画比较简单,不做多介绍,只留笔记重点。如果有误,欢迎指正。
    //A.CABasicAnimation
    //CABasicAnimation(keyPath: "transform")可以实现2D和3D动画。取决于keyPath。
    //如果keyPath: "transform" 则是3D动画。rotation属性才能体现3D效果
    //如果keyPath: "transform.rotation" 则是2D动画。
    //2D动画变换前的原始状态。view.transform = CGAffineTransformIdentity
    //3D动画变换前的原始状态view.layer.transform = CATransform3DIdentity
  • CAKeyFrameAnimation动画功能强大,有必要对其属性阐述一下。
    //B. CAKeyFrameAnimation
    //CApropertyAnimation的子类,跟CABasicAnimation的区别是: CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值
    //属性解析:
    //values:就是上述的NSArray对象。里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
    //path:可以设置一个CGPathRef\CGMutablePathRef,让层跟着路径移动。path只对CALayer的anchorPoint和position起作用。如果你设置了path,那么values将被忽略
    //keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是平分的
    //CABasicAnimation可看做是最多只有2个关键帧的CAKeyframeAnimation
    //这里有必要提供一下快速构建values的方法
    let arr = [(20,30),(100,100),(100,300),(50,300)].map{ (x:Int,y:Int) -> NSValue in
    NSValue(CGPoint: CGPoint(x: x, y: y))

      }
      
      keyAnimate.values = arr
    
  • CAAnimationGroup也比较简单,就是对多个动画的组合。
    //C. CAAnimationGroup
    //CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行.支持多个动画组合。
    //属性解析:
    //animations:用来保存一组动画对象的NSArray
    //默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间

  • CATransition是一个比较有意思的动画,转场效果挺多。
    //D. CATransition
    //CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点
    //UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果
    //属性解析:
    //type:动画过渡类型
    /*
    fade
    push
    moveIn
    reveal
    cube
    oglFlip
    suckEffect
    rippleEffect
    pageCurl
    pageUnCurl
    cameraIrisHollowOpen
    cameraIrisHollowClose
    */
    //subtype:动画过渡方向
    //startProgress:动画起点(在整体动画的百分比)
    //endProgress:动画终点(在整体动画的百分比)

  • CASpringAnimation是弹性动画,能够表现出非常性感细腻的效果
    //E.CASpringAnimation
    属性:默认值
    damping:10.0
    mass :1.0
    stiffness:100.0
    initialVelocity:0.0

     let sprintAni = CASpringAnimation(keyPath: "position.y")
      sprintAni.damping = 10
      sprintAni.mass = 5
      sprintAni.stiffness = 50
      sprintAni.initialVelocity = 3
      
      sprintAni.duration = 2
      sprintAni.toValue = 300
          
      sprintAni.fillMode = kCAFillModeForwards
      sprintAni.removedOnCompletion = false
      
      yourView.layer.addAnimation(sprintAni, forKey: "anykey")
    

以上是CA动画的类型,我学习完它再去学UIView动画,所以知道UIView动画其实就是CA动画的封装,优点是快捷方便,UIView的弹性动画完美体现了这点。缺点是不能细微调整。这里不作UIView的详细介绍。

虽然UIView动画是对CA核心动画的封装,但还是有必要对他们加以总结,这可是目前在网上找不到的宝贵经验哦!(经验可能有误,欢迎指正)

//UIView的动画跟CAAnimation动画的异同:
//1.都能控制动画开始执行时刻。uiview的delay。CA中的beginTime。
//2.都能在动画结束后实现控制。uiview有闭包。CA中有代理函数didFinish。
//3.uiView有弹性动画和关键帧动画,CA中也有,而且更为丰富。
//4.uiView组合动画用cgaffinetransformconcat。CA中用CAanimationGroup,并支持多组合。
//    uiview 的多组合则可以通过创建多个uiview.animation来实现。多是指两个以上。
//    这里的组合是指为同一个对象的不同属性进行组合。
//    如果是不同对象要实现同一个动画,则直接在uiview的内容中添加。或者直接在CA中赋予多个对象的layer。
//5.uiView实现2D或者3D动画,取决于里面设置的动画属性。若设置的是layer层,则可以实现3D动画。(rotation属性可以体现)
//  CA动画则取决于key。如果是transform,则可以3D.如果是transform.rotation,则可以是2D。
//    CA中transform属性有rotation,scale,translation。
//    CA中key还可以是bounds,position,opacity。这里的position等价于uiview的center。
//6.uiview动画完毕之后属性已经更改。CA动画则不会改变实际位置,即使表面改变了。

下面我则要记录下我在学习UIView动画的时候是怎么给自己挖坑的,并怎么最终把坑填上获得小小成就感的。其实当完美解决问题的那一刻,发现代码是如此的简单,可就为了那一段代码,让我费劲了力气,花尽了脑汁才得以解决。只怪自己经验不足咯!都说怪我咯希望能帮助到有同样困惑的人儿

关键字:中断,终止,中止,取消,停止UIView动画

问题发现:

  • UIView动画在duration内,也就是正在执行的过程中,我再次触发了同样的动画,此时动画就会不正常显示。

问题起源:

  • 发现这个问题的时候,其实很多人就想到可能会转用CA动画去实现,因为CA动画在执行过程中,再次触发的话,它会重新来过,并不会出现错乱。我也是想到了这个办法,但是我就想知道在UIView动画中怎么解决这个问题的!所以问题就这样起源了~

问题解决:

  • 1.首先肯定是想到再次触发前先把上一次动画取消掉,想想应该是很快就把问题给解决了吧,因为从逻辑上并没有什么错误。于是我触发的前面加了一句self.textView?.layer.removeAllAnimations()
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {      
        self.textView?.layer.removeAllAnimations()
        self.animation()   
    }

可是问题真的解决了吗?当然不是。添加后的现象是我再次触发时,动画立马停止了。看起来后面self.animation()并没有执行一样。

  • 2.于是我开始请教百度叫兽,失望的是几乎把百度翻了个遍,也没找到答案。纳闷了,难道只有我才遇到这个问题吗?只有我经验浅脑子笨才掉这个坑吗?唯一在网上找到一个相关的文章《如何中止UIView动画?》
    http://samwei12.gitcafe.io/2015/09/09/%E5%A6%82%E4%BD%95%E5%8F%96%E6%B6%88UIView%E5%8A%A8%E7%94%BB/ 简书上也有。又是OC版本的,OC就OC吧,抱着一线希望把OC代码转换成Swift后,一执行丫的还是不管用!梦想再次破灭~

  • 3.这时想到了swift交流群,在群上一问三不知,这该如何是好。大神都不出来帮我~

  • 4.还是自己找原因吧。在UIView动画执行完的闭包里面添加一些打印信息吧。于是我添加了print("finish")
    { (finish:Bool) -> Void in
    if finish {print("finish")}
    }
    此时我在第二次又触发动画的时候发现,只打印了一次finish!这finish是第一次动画执行的还是第二次动画执行的?从现象上就很好解释了,肯定是第二次动画打印的finish。而且还有一个现象就是,第二次触发动画的时候,立马就打印finish,这也就是为什么看不到第二次动画的执行!原来本意是要停掉上一次正在执行的动画,再接着执行第二次动画。现在问题是第二次也被停掉了!!!到底问题出现在哪里???灵光一闪,突然想到了延时!就是停掉第一次动画的时候,延时一下,再执行第二次动画看行不行?
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    self.textView?.layer.removeAllAnimations()
    self.performSelector("animation", withObject: nil, afterDelay: 0.3)
    }

  • 5.duang~的一下,成功了!只要在执行动画的前面添加一个细小的延时,就可以完美解决了这个可恶的问题!后面我再测了一下,把afterDelay改成0 ,也同样成功了,这这又如何解释,就留给大家吧

问题回首:

  • 解决的代码非常简单,可对我来说真的容易么?在没有百度君简书君的帮助下,孤军奋战,血战到底,我容易么?
有了UIView动画的填坑经验,我自个再到CA中去解决类似问题就迎刃而解了!什么?刚刚不是说CA中不存在这种问题吗??这里有必要说明一点,就是当CA动画是非无止境动画(就是会停止的动画),在还没停止之前再次触发,是不会发生这些错乱问题的。

然而要是CA动画是个无止境的动画,也就是如果动画委托协议的animationDidStop中再次调用动画函数的话,这时再来个触发相同动画,动画就是乱得一塌糊涂了!接下来就记录一下怎么轻松解决这个问题的。
   @IBAction func next(sender: AnyObject) {
    self.transition()
    }

  func transition(){
    let transition = CATransition()
    transition.delegate = self
    //动画过渡类型
    transition.type = "pageCurl"
    
    //动画过渡类型方向
    transition.subtype = kCATransitionFromLeft
    
    transition.duration = 1
    
    transition.setValue("second", forKey: "whichAnimation")
    
    self.iv.layer.addAnimation(transition, forKey: nil)
  }

  override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
        switch anim.valueForKey("whichAnimation") as! String{
        case "one"    :
                        print("hello")         
        case "second" :
                        print("finish")    
                        self.next("repeat")
        default :print("grandre")
        }
  }

这样的代码确实能够运行,能够循环调用动画,实现无止境。但是问题是当再次点击按键触发动画的话,这代码的bug就一漏无遗了。

  • 解决初探1:再次触发之前,去掉所有动画。
    @IBAction func next(sender: AnyObject) {
    self.iv.layer.removeAllAnimations()
    self.transition()
    }
    结果:失败。原因:self.iv.layer.removeAllAnimations()执行后会调用委托协议,导致死循环。

  • 解决初探2:添加“是否自动完成动画”标志。如果是自动完成一轮动画,则执行委托协议代码,如果不是自动完成,则不执行。从而避免了死循环。
    @IBAction func next(sender: AnyObject) {
    ifAutoFinishAnimate = false
    self.iv.layer.removeAllAnimations()//这里没打印是因为标志置false了
    self.transition()
    }
    func transition(){
    let transition = CATransition()
    transition.delegate = self
    //动画过渡类型
    transition.type = "pageCurl"

      //动画过渡类型方向
      transition.subtype = kCATransitionFromLeft
      
      transition.duration = 1
      //        一定要在加载动画之前设置setValue
      transition.setValue("second", forKey: "whichAnimation")
      
      self.iv.layer.addAnimation(transition, forKey: nil)
      ifAutoFinishAnimate = true  //动画完成之后恢复标志,才能执行委托协议代码
    }
      override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
       if ifAutoFinishAnimate == true{
          switch anim.valueForKey("whichAnimation") as! String{
          case "one" :print("hello")
          case "second" :print("finish")
              self.next("2")
          default :print("baba")
          }
        }
     }
    

    结果:失败!现象是“根本停不下来!”此时原因应该就是UIView动画的原因一样了!


  • 解决初探3:添加延时。
    @IBAction func next(sender: AnyObject) {
    ifAutoFinishAnimate = false
    self.iv.layer.removeAllAnimations()//这里没打印是因为标志置false了
    performSelector("transition", withObject: nil, afterDelay: 0.3)
    }
    结果:Done!完美解决!这经验真管用!afterDelay改成0,这次就不行咯!至于为什么,同样留给大家思考吧。所以记录所遇到的问题并加以总结是对自己非常有帮助的。

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

推荐阅读更多精彩内容

  • Core Animation Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,...
    45b645c5912e阅读 2,996评论 0 21
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,429评论 6 30
  • 显式动画 显式动画,它能够对一些属性做指定的自定义动画,或者创建非线性动画,比如沿着任意一条曲线移动。 属性动画 ...
    清风沐沐阅读 1,909评论 1 5
  • 如果想让事情变得顺利,只有靠自己--夏尔·纪尧姆 上一章介绍了隐式动画的概念。隐式动画是在iOS平台创建动态用户界...
    夜空下最亮的亮点阅读 1,901评论 0 1
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,075评论 5 13