iOS核心动画高级技巧之核心动画(三)
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就可以让它动画执行后保持在原界面,不用回到初始值.
动画速度
设置CAAnimation的timingFunction属性可以控制动画的速度,而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核心动画高级技巧之核心动画(三)的更多相关文章
- iOS核心动画高级技巧之图层变换和专用图层(二)
iOS核心动画高级技巧之CALayer(一) iOS核心动画高级技巧之图层变换和专用图层(二)iOS核心动画高级技巧之核心动画(三)iOS核心动画高级技巧之性能(四)iOS核心动画高级技巧之动画总结( ...
- iOS核心动画高级技巧之CALayer(一)
iOS核心动画高级技巧之CALayer(一) iOS核心动画高级技巧之图层变换和专用图层(二)iOS核心动画高级技巧之核心动画(三)iOS核心动画高级技巧之性能(四)iOS核心动画高级技巧之动画总结( ...
- iOS核心动画高级技巧 - 8
iOS核心动画高级技巧 - 1 iOS核心动画高级技巧 - 2 iOS核心动画高级技巧 - 3 iOS核心动画高级技巧 - 4 iOS核心动画高级技巧 - 5 iOS核心动画高级技巧 - 6 iOS核 ...
- iOS核心动画高级技巧 - 6
11. 基于定时器的动画 基于定时器的动画 我可以指导你,但是你必须按照我说的做. -- 骇客帝国 在第10章“缓冲”中,我们研究了CAMediaTimingFunction,它是一个通过控制动画缓冲 ...
- iOS核心动画高级技巧-5
9. 图层时间 图层时间 时间和空间最大的区别在于,时间不能被复用 -- 弗斯特梅里克 在上面两章中,我们探讨了可以用CAAnimation和它的子类实现的多种图层动画.动画的发生是需要持续一段时间的 ...
- iOS核心动画高级技巧-4
8. 显式动画 显式动画 如果想让事情变得顺利,只有靠自己 -- 夏尔·纪尧姆 上一章介绍了隐式动画的概念.隐式动画是在iOS平台创建动态用户界面的一种直接方式,也是UIKit动画机制的基础,不过它并 ...
- iOS核心动画高级技巧 - 3
7. 隐式动画 隐式动画 按照我的意思去做,而不是我说的. -- 埃德娜,辛普森 我们在第一部分讨论了Core Animation除了动画之外可以做到的任何事情.但是动画是Core Animation ...
- iOS动画篇:核心动画
转:http://www.cocoachina.com/ios/20160517/16290.html 基本概念 1.什么是核心动画 Core Animation(核心动画)是一组功能强大.效果华丽的 ...
- ios开发核心动画七:核心动画与UIView动画的区别
/** UIView与核心动画区别?(掌握) 1.核心动画只作用在layer. 2.核心动画看到的都是假像,它并没有去修改UIView的真实位置. 什么时候使用核心动画? 1.当不需要与用户进行交互, ...
随机推荐
- 【Hadoop】hiveserver2 不能启动端口 10000 开启服务的相关经验总结
转载来自http://blog.csdn.net/lsttoy/article/details/53490144. 这个问题困扰了我三天,各种查资料踩坑填坑的尝试,终于搞定了这个问题. 首先来品尝下喜 ...
- Prim算法:最小生成树---贪心算法的实现
算法图解: http://baike.baidu.com/link?url=hGNkWIOLRJ_LDWMJRECxCPKUw7pI3s8AH5kj-944RwgeBSa9hGpTaIz5aWYsl_ ...
- UT源码116
2)NextDate函数问题 NextDate函数说明一种复杂的关系,即输入变量之间逻辑关系的复杂性 NextDate函数包含三个变量month.day和year,函数的输出为输入日期后一天的日期. ...
- Mathematics Base - 期望、方差、协方差、相关系数总结
参考:<深度学习500问> 期望 在概率论和统计学中,数学期望(或均值,亦简称期望)是试验中每次可能结果的概率乘以其结果的总和.它反映随机变量平均取值的大小. 线性运算: \(E(ax+ ...
- wpf listboxitem添加下划线
1.通过List<string>进行赋值,没有字段绑定 // 前台xaml <ListBox x:Name="list1"> <ListBox.Ite ...
- 利用CSS制作脸书
很多网站都支持图片上的头像框识别,鼠标在头像框处,会提示一些人物信息. 这次就利用CSS实现这样一个功能: div处主要包括两部分,一部分是图片:另一部分是链接以及脸框 <div class=& ...
- Dedecms本地上传缩略图无法自动添加水印的解决方法
客户遇到一个问题,DEDECMS(V5.7)后台添加文档时,本地上传缩略图无法自动添加水印(系统设置里的图片水印设置没有问题),找了半天,终于找到了解决方法,留个记号: 打开dede/archives ...
- Java 8 Optional类使用的实践经验
前言 Java中空指针异常(NPE)一直是令开发者头疼的问题.Java 8引入了一个新的Optional类,使用该类可以尽可能地防止出现空指针异常. Optional 类是一个可以为null的容器对象 ...
- oracle odbc连接sqlserver 无法查询字段
最近因项目需要,需要在oracle数据库里面通过DBLINK方式链接SQLserver数据库(oracle 11G,Sqlserver2016,具体实现参考链接 http://blog.sina.co ...
- Leetcode初级算法(字符串篇)
目录 反转字符串 颠倒整数 字符串中的第一个唯一字符 有效的字母异位词 验证回文字符串 实现strStr() 数数并说 最长公共前缀 字符串转整数(atoi) 反转字符串 和vector同样的进行sw ...