iOS核心动画高级技巧之核心动画(三)

时间:2021-05-19 00:55:31

iOS核心动画高级技巧之CALayer(一)

iOS核心动画高级技巧之图层变换和专用图层(二)
iOS核心动画高级技巧之核心动画(三)
iOS核心动画高级技巧之性能(四)
iOS核心动画高级技巧之动画总结(五)

隐式动画

  隐式动画主要作用于CALayer的可动画属性上面,UIView对应的layer是不可以的,只要你改变属性的值,它不是突兀的直接改变过去,而是一个有一个动画的过程,这个时间等属性你可以通过事务(CATransaction)来控制,如果你不自己提供一个事务,它的默认时间是0.25秒,当然这个可动画属性是需要触发的,如果你一上来就设置一个值,可能看不到动画效果.

 redLayer = CALayer()
redLayer.backgroundColor = UIColor.redColor().CGColor
redLayer.frame = CGRectMake(, , , )
self.view.layer.addSublayer(redLayer) NSTimer.scheduledTimerWithTimeInterval(, target: self, selector: NSSelectorFromString("animate"), userInfo: nil, repeats: false) func animate() {
CATransaction.begin()
CATransaction.setAnimationDuration() var redC = CGFloat(arc4random() % ) / 255.0
var greenC = CGFloat(arc4random() % ) / 255.0
var blueC = CGFloat(arc4random() % ) / 255.0 self.redLayer.backgroundColor = UIColor(red: redC, green: greenC, blue: blueC, alpha: ).CGColor CATransaction.commit()
}

  在上面这个transaction中加上一个完成块可以使它在在动画完成的时候做一个0.25秒的默认动画,demo中有两种动画方式都可以(2d和3d)

 CATransaction.begin()
CATransaction.setAnimationDuration()
CATransaction.setCompletionBlock { () -> Void in
/*3d动画
var transform = self.redLayer.transform
transform = CATransform3DRotate(transform, CGFloat(M_PI_4), 0, 0, 1)
self.redLayer.transform = transform
*/
//这个是2d的动画
var transform = self.redLayer.affineTransform()
transform = CGAffineTransformRotate(transform, CGFloat(M_PI_4))
self.redLayer.setAffineTransform(transform)

  layer之所以能做隐式动画是因为对应的属性有对应的action,这个action可以通过layer的delegate的代理方法actionforLayer:forkey获得,也可以通过设置layer的actions属性实现,两种方法都之所以UIView没有隐式动画,是因为它对应的layer对应的delegate是它自己,而它的actionforlayer:forkey方法每次都是返回nil,所以它没有对应的action,所以不能做隐式动画,如果你想让一个view有隐式动画的话可以重写它的actionforlayer方法,跟它对应的key返回一个action,这个action的类型是CAtransition类型.另外,在UIView的beginAnimations和commitAnimations方法中间它的actionfoylayer方法会有返回值可以做隐式动画,如果你不想让普通的layer做隐式动画可以调用CATransaction的setDisableActions方法禁止,直接设置actions为nil没什么反应

 var transition = CATransition()
transition.type = kCATransitionPush//设置出现方式,默认为fade
transition.subtype = kCATransitionFromLeft//设置出现方向
redLayer.actions = ["backgroundColor":transition]

  layer能做隐式动画,但是你获取可动画的属性它的值还是最后设置的值,那是因为它并不是一个layer生成的动画,它有modelLayer一般是返回layer本身,它还有一个presenttationLayer呈现图层,我们设置的值是给modelLayer的,而做动画的是presentationLayer.有两种情况你可能需要用到呈现图层,一种是你需要获得动画过程中layer的位置,另一种是你需要在动画过程中响应用户交互.下面这个demo效果是如果你点击到了方块则变颜色,没点到则方块移动到你点击的位置

 redLayer = CALayer()
redLayer.backgroundColor = UIColor.redColor().CGColor
redLayer.frame = CGRectMake(, , , )
self.view.layer.addSublayer(redLayer) override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var point = ( touches as NSSet ) .anyObject()?.locationInView(self.view) if (self.redLayer.presentationLayer().hitTest(point!) != nil) {
var redC = CGFloat(arc4random() % ) / 255.0
var greenC = CGFloat(arc4random() % ) / 255.0
var blueC = CGFloat(arc4random() % ) / 255.0 self.redLayer.backgroundColor = UIColor(red: redC, green: greenC, blue: blueC, alpha: ).CGColor
}else {
CATransaction.begin()
CATransaction.setAnimationDuration(2.0)
self.redLayer.position = point!
CATransaction.commit()
}
}

  显式动画

CABasicAnimation(属性动画) 

  CABasicAnimation动画和隐式动画类似,它可以设置起始和结束值和delegate,下面这个例子和上面的隐式动画类似

     redLayer = CALayer()
redLayer.backgroundColor = UIColor.redColor().CGColor
redLayer.frame = CGRectMake(, , , )
self.view.layer.addSublayer(redLayer) NSTimer.scheduledTimerWithTimeInterval(, target: self, selector: NSSelectorFromString("animate"), userInfo: nil, repeats: false)
} func animate () {
var redC = CGFloat(arc4random() % ) / 255.0
var greenC = CGFloat(arc4random() % ) / 255.0
var blueC = CGFloat(arc4random() % ) / 255.0
var color = UIColor(red: redC, green: greenC, blue: blueC, alpha: ).CGColor var animate = CABasicAnimation()
animate.duration = 8.0
animate.keyPath = "backgroundColor"
animate.toValue = color
animate.delegate = self
self.redLayer .addAnimation(animate, forKey: nil)
} override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
CATransaction.begin() CATransaction.setDisableActions(true)
var animate = anim as! CABasicAnimation
self.redLayer.backgroundColor = animate.toValue as! CGColorRef
CATransaction.commit()
}

  注意在代码中,stop的代理方法中的隐式动画我们是禁用的,要不然它会做两次动画,如果是view的layer动画就不需要,因为view的隐式动画默认就禁止了,如果有多个动画需要代理方法,可以在添加动画的时候设置key,在代理方法中通过key获取animate.还有更简便的KVC来获取,animate.setValue(redView,forKey:"redView"),在代理方法中用valueForKey获取

CAKeyframeAnimation(关键帧动画)

  关键帧动画见名知意,你设置values属性中的每一帧的值,然后iOS就会按照你设置的值来做动画,你还可以对应设置它对应的时间,这个很强大后面会说到,现在先说另一个强大的功能,它可以沿着路径来做动画,只要跟它的path属性设置一个CGBezierPathRef类型的值就可以了.其实沿着路径做动画就和设置一个个values的值是一样的,路径也是由一个个position的值的点组成的,沿着路径做动画,物体要跟着路径调整方向,你可以同时跟它的方向做rotate动画,但是可能引起冲突或其它问题,它又一个rotationMode 直接设置成rotateAuto就可以了.demo很简单,列举了.

  还有一点需要注意的是:在做transform做动画时用transform.rotation等来做动画会好很多,一方面可以用byValue来设置值,另一方面position/scale/rotation也不会有冲突.你直接设置transform.position的值其实本身是没有用的,因为它就没有这个属性,只是iOS内部用KVC把transform.position的值用CAValueFuction转换成了transform对应的矩阵值

CAAnimationGroup(组动画)

  CAAnimationGroup动画组也是顾名思义可以做一个动画组,只要跟它的animations属性设置一个动画数组就行了,每个数组项的值是一个basicAnimation或者keyframeAnimation

CATransition(过渡动画)

  这里首先需要注意的是CATransition是一个动画,而CATransaction是一个动画事务,都是核心动画里面的两个概念,所以要区别开来.应该说CATransition是核心动画里最简单最好用但是最容易被人忽略的动画了,下面跟imageview设置image的时候给一个fadein的动画,你看是多简单.

    NSTimer.scheduledTimerWithTimeInterval(, target: self, selector: NSSelectorFromString("animate"), userInfo: nil, repeats: false)

     self.img = UIImageView(image: UIImage(named: "111.png"))
self.view.addSubview(self.img)
} func animate () {
var transition = CATransition()
transition.type = kCATransitionFade
self.img.layer .addAnimation(transition, forKey: nil) self.img.image = UIImage(named: "222.png")
}

  对于CAtransition来说,在自己创建的layer中,它是默认加上的,而在view关联的屠城,它是被禁用的,毕竟它还是得提供你一种简单的正常的设置属性的方法,它是对整个图层树都有效,如果添加了transition会跟它的子图层都加上整个效果,比如跟tabbar切换的渐变效果(大多数VC切换都可以在代理方法或其它方法中写动画)

     var root = UITabBarController()
root.viewControllers = [ViewController(),OneViewController()]
root.delegate = self
self.rootVC = root self.window?.rootViewController = root
self.window?.makeKeyAndVisible() return true
}
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
var transition = CATransition()
transition.type = kCATransitionFade
transition.duration =
self.rootVC.view.layer.addAnimation(transition, forKey: nil)
}

  UiView提供了transitionFromView的方法,如果只跟一个view做动画直接可以用它和加上transition动画的效果是一样的,但是一般它对子图层不起作用,在动画运行的过程中可以remove掉动画,一个按钮添加动画,一个按钮控制删除动画,而动画是使用的byValue,所以效果就是看起来是暂停.

  下面的代码做的是暂停动画的功能,在暂停的时候更新model树为presentlayer的值.

     redLayer = CALayer()
redLayer.backgroundColor = UIColor.redColor().CGColor
redLayer.frame = CGRectMake(, , , )
self.view.layer.addSublayer(redLayer) var beginBtn = UIButton(frame: CGRectMake(, , , ))
beginBtn.setTitle("开始", forState: UIControlState.Normal)
beginBtn.addTarget(self, action: "begin", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(beginBtn) var stopBtn = UIButton(frame: CGRectMake(, , , ))
stopBtn.setTitle("暂停", forState: UIControlState.Normal)
stopBtn.addTarget(self, action: "stop", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(stopBtn)
} func begin() {
var animate = CABasicAnimation()
animate.keyPath = "transform.rotation"
animate.duration = 2.0
animate.byValue = CGFloat(M_PI / )
animate.delegate = self
// animate.fillMode = kCAFillModeForwards
// animate.removedOnCompletion = false
self.redLayer .addAnimation(animate, forKey: "animate")
}
func stop() {
self.redLayer.transform = self.redLayer.presentationLayer().transform
self.redLayer.removeAnimationForKey("animate")
}

图层时间

  beginTime/speed/timeoffset 三个动画属性都是相对概念,分别表示对应duration的开始时间,动画的速度(会改变动画结束的时间),让动画瞬间快进到某一点.下面是让动画暂停的第二种方法.

     redLayer = CALayer()
redLayer.backgroundColor = UIColor.redColor().CGColor
redLayer.frame = CGRectMake(, , , )
self.view.layer.addSublayer(redLayer) var beginBtn = UIButton(frame: CGRectMake(, , , ))
beginBtn.setTitle("开始", forState: UIControlState.Normal)
beginBtn.addTarget(self, action: "resumeLayer", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(beginBtn) var stopBtn = UIButton(frame: CGRectMake(, , , ))
stopBtn.setTitle("暂停", forState: UIControlState.Normal)
stopBtn.addTarget(self, action: "pauseLayer", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(stopBtn) self.pauseTime = 0.0
self.startAnimate()
} func startAnimate () {
var animate = CABasicAnimation()
animate.keyPath = "transform.rotation"
animate.duration = 20.0
animate.byValue = CGFloat(M_PI * )
animate.delegate = self
self.redLayer .addAnimation(animate, forKey: "animate")
} func pauseLayer () {
//获取当前动画的时间
self.pauseTime = self.redLayer.convertTime(CACurrentMediaTime(), fromLayer: nil)
//停止运动
self.redLayer.speed = 0.0
//设置它呆在目前的状态不变,不然因为speed为0,layer变回了最初动画的值
self.redLayer.timeOffset = self.pauseTime
} func resumeLayer () {
//获取暂停开始的时间
var pausedTime = self.pauseTime
//设置速度timeOffset等为正常值
self.redLayer.speed = 1.0
self.redLayer.timeOffset = 0.0
self.redLayer.beginTime = 0.0 var timeSincePause = self.redLayer.convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime
//设置开始时间为初始和现在的时间差
self.redLayer.beginTime = timeSincePause
}

  removeOnCompletion 设置为no,将会在动画结束后仍然保持前一步的状态,然后把fillMode设置成modeforwards就可以让它动画执行后保持在原界面,不用回到初始值.

动画速度

  设置CAAnimationtimingFunction属性可以控制动画的速度,而CAKeyframeAnimation有一个NSArray类型的timingFunctions属性,它需要values这个数组长度和它的长度比它大1,这样就可以控制每一段动画的速度了。timingFunction的值是CAMediaTimingFunction类型的,它还有一个初始化方法可以自定义时间曲线,它是一个三次贝塞尔缓冲函数,可以通过起始点、终点、两个控制点来初始化,默认的CAMediaTimingFunction值其实可以这样初始化得来。

  对于设置values数组长度为5,timingFunctions数组长度也为4,相当于做了四段关键帧动画,而这每段动画的时间曲线由timingFunction的每个项来控制,而这4段动画的时间则是均分during的时间,你还可以设置keyTimes的值,它也是一个数组,它和timingFunction的数组长度一样,控制每段动画的时间。用这个特性几乎可以做大多数规则动画了。

  通过上面的方法我们已经基本能够做任何动画了,但是我要做一个弹簧效果或者球落地的效果都需要设置多个values和timingFunction和keytimes,而且需要精确计算还效果不逼真。所以我们直接可以设置它的values值就是动画的路径,由于时间是平均的,而values值不同,最后就会产生速度上的差异,就会产生绚丽的效果。产生路径的函数这个网站有提供:http://www.timotheegroleau.com/Flash/experiments/easing_function_generator.htm

定时器

  上面我们说过设置一系列的values,然后根据时间间隔相同来做动画,既然添加这个关键帧动画只是让它在每个相同的时间内运动一定的已知的距离,这直接用NSTime就可以解决了,每一次遍历values数组的值,让它位移到那就可以了。其实这就是核心动画的本质,它存在一个问题就是NSTime是添加到NSRunloop中的,而iOS每个线程管理着一个NSRUNloop,它的每次事件是添加到任务列表里去,权限比较低,如果一个屏幕有很多动画的话就有可能有延迟,然后就可能出现卡顿的现象,动画就不流畅,你还可以用CADisplayLink代替它,它和NSTime的原理是一样的,都是 一定时间执行一个方法,而且他们可以设置自己的优先级,而不同的是NSTime是被添加到任务列表中,它在屏幕刷新的时候就一定会调用一次,屏幕刷新率一般是60次每秒,而NSTime是添加到任务列表中,它会被其它任务阻塞,在主线程的任务有 处理触摸事件、发送和接受网络数据包、执行使用gcd的代码、处理计时器行为、屏幕重绘等。FB的POP框架也是使用了CADisplayLink。

iOS核心动画高级技巧之核心动画(三)的更多相关文章

  1. iOS核心动画高级技巧之图层变换和专用图层&lpar;二&rpar;

    iOS核心动画高级技巧之CALayer(一) iOS核心动画高级技巧之图层变换和专用图层(二)iOS核心动画高级技巧之核心动画(三)iOS核心动画高级技巧之性能(四)iOS核心动画高级技巧之动画总结( ...

  2. iOS核心动画高级技巧之CALayer&lpar;一&rpar;

    iOS核心动画高级技巧之CALayer(一) iOS核心动画高级技巧之图层变换和专用图层(二)iOS核心动画高级技巧之核心动画(三)iOS核心动画高级技巧之性能(四)iOS核心动画高级技巧之动画总结( ...

  3. iOS核心动画高级技巧 - 8

    iOS核心动画高级技巧 - 1 iOS核心动画高级技巧 - 2 iOS核心动画高级技巧 - 3 iOS核心动画高级技巧 - 4 iOS核心动画高级技巧 - 5 iOS核心动画高级技巧 - 6 iOS核 ...

  4. iOS核心动画高级技巧 - 6

    11. 基于定时器的动画 基于定时器的动画 我可以指导你,但是你必须按照我说的做. -- 骇客帝国 在第10章“缓冲”中,我们研究了CAMediaTimingFunction,它是一个通过控制动画缓冲 ...

  5. iOS核心动画高级技巧-5

    9. 图层时间 图层时间 时间和空间最大的区别在于,时间不能被复用 -- 弗斯特梅里克 在上面两章中,我们探讨了可以用CAAnimation和它的子类实现的多种图层动画.动画的发生是需要持续一段时间的 ...

  6. iOS核心动画高级技巧-4

    8. 显式动画 显式动画 如果想让事情变得顺利,只有靠自己 -- 夏尔·纪尧姆 上一章介绍了隐式动画的概念.隐式动画是在iOS平台创建动态用户界面的一种直接方式,也是UIKit动画机制的基础,不过它并 ...

  7. iOS核心动画高级技巧 - 3

    7. 隐式动画 隐式动画 按照我的意思去做,而不是我说的. -- 埃德娜,辛普森 我们在第一部分讨论了Core Animation除了动画之外可以做到的任何事情.但是动画是Core Animation ...

  8. iOS动画篇:核心动画

    转:http://www.cocoachina.com/ios/20160517/16290.html 基本概念 1.什么是核心动画 Core Animation(核心动画)是一组功能强大.效果华丽的 ...

  9. ios开发核心动画七:核心动画与UIView动画的区别

    /** UIView与核心动画区别?(掌握) 1.核心动画只作用在layer. 2.核心动画看到的都是假像,它并没有去修改UIView的真实位置. 什么时候使用核心动画? 1.当不需要与用户进行交互, ...

随机推荐

  1. SpringMVC与MyBatis整合之日期格式转换

    在上一篇博客<SpringMVC与MyBatis整合(一)——查询人员列表>中遗留了日期格式转换的问题,在这篇记录解决过程. 对于controller形参中pojo对象,如果属性中有日期类 ...

  2. &lbrack;CareerCup&rsqb; 15&period;4 Types of Join 各种交

    15.4 What are the different types of joins? Please explain how they differ and why certain types are ...

  3. SAP RFC通信模式

    在网络技术中,数据通信可以大致划分为两种基本模式:同步通信和异步通信. 其本义是:异步通信时,通信双方时钟允许存在一定误差:同步通信时,双方时钟的允许误差较小.在SAP的系统间的通信过程中,也借用术语 ...

  4. linux笔记:RPM软件包管理-rpm命令管理

    rpm包命名原则: rpm包的依赖性: 包名和包全名: rpm软件包安装.升级和卸载: rpm软件包查询: 从rpm包中提取指定文件:

  5. poj2749

    万变不离其宗 只要搞清楚题目的基本模型 搞清楚边是一种推导出的关系 搞清楚里面的逻辑关系 那就没什么难的了…… 二分+sat,没什么好说的 ; type node=record point,next: ...

  6. git提交如何忽略某些文件

    在使用git对项目进行版本管理的时候,我们总有一些不需要提交到版本库里的文件和文件夹,这个时候我们就需要让git自动忽略掉一下文件. 使用.gitignore忽略文件 为了让git忽略指定的文件和文件 ...

  7. Android TCP&sol;IP 扫盲教程

    TCP/IP 是因特网的通信协议. 通信协议是对计算机必须遵守的规则的描写叙述.仅仅有遵守这些规则.计算机之间才干进行通信. 浏览器和server都在使用 TCP/IP 因特网浏览器和因特网serve ...

  8. Web前端有价值的博客文章汇总

    一.HTML 二.CSS 1.深入理解position和z-index属性 :https://www.cnblogs.com/zhuzhenwei918/p/6112034.html 2.BFC(清除 ...

  9. 微信小程序 - 相对定位和绝对定位 - 相对路径和绝对路径

    微信小程序 - 相对定位和绝对定位 相对定位relative,绝对定位absolute 相对定位:元素是相对自身进行定位,参照物是自己. 绝对定位:元素是相对离它最近的一个父级元素进行定位. 相对定位 ...

  10. Spring4新特性——集成Bean Validation 1&period;1&lpar;JSR-349&rpar;到SpringMVC

    在之前的<跟我学SpringMVC>中的<第七章 注解式控制器的数据验证.类型转换及格式化>中已经介绍过SpringMVC集成Bean Validation 1.0(JSR-3 ...