Core Animation总结(一)图层变换(平面 立体)html
Core Animation总结(四)swift
#Core Animationdom
###隐式动画 #####事务 Core Animation基于一个假设,说屏幕上的任何东西均可以(或者可能)作动画。动画并不须要你在Core Animation中手动打开,相反须要明确地关闭,不然他会一直存在。ide
隐式动画 是由于咱们并无指定任何动画的类型。咱们仅仅改变了一个属性,而后Core Animation来决定如何而且什么时候去作动画,动画执行的时间取决于当前事务的设置,动画类型取决于图层行为函数
事务其实是Core Animation用来包含一系列属性动画集合的机制,任何用指定事务去改变能够作动画的图层属性都不会马上发生变化,而是当事务一旦提交的时候开始用一个动画过渡到新值。oop
事务是经过CATransaction类来作管理,任何能够作动画的图层属性都会被添加到栈顶的事务,你能够经过+setAnimationDuration:方法设置当前事务的动画时间,或者经过+animationDuration方法来获取值(默认0.25秒)。
Core Animation在每一个run loop周期中自动开始一次新的事务(run loop是iOS负责收集用户输入,处理定时器或者网络事件而且从新绘制屏幕的东西),即便你不显式的用[CATransaction begin]开始一次事务,任何在一次run loop循环中属性的改变都会被集中起来,而后作一次0.25秒的动画。
使用事务 随机改变图层颜色,若是不使用colorLayer,直接用UIView.layer就没有平滑过渡的动画 Core Animation一般对CALayer的全部属性(可动画的属性)作动画,可是UIView关联的图层禁用了隐式动画
var colorLayer: CALayer! override func viewDidLoad() { super.viewDidLoad() colorLayer = CALayer() colorLayer.frame = CGRect(x: 50, y: 50, width: 100, height: 100) colorLayer.backgroundColor = UIColor.blue.cgColor self.view.layer.addSublayer(colorLayer) // 使用推动过渡的色值动画 let transition: CATransition = CATransition() transition.type = kCATransitionPush transition.subtype = kCATransitionFromLeft colorLayer.actions = ["backgroundColor":transition] self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapAction(sender:)))) } /// 使用事务 随机改变图层颜色 func tapAction(sender:UIGestureRecognizer){ // 开始一个新的事务 CATransaction.begin() // 设置动画持续时间 CATransaction.setAnimationDuration(1.0) // 完成块是在颜色渐变的事务提交并出栈以后才被执行,因而,用默认的事务作变换,默认的时间也就变成了0.25秒。 CATransaction.setCompletionBlock { // 旋转90动画 var transform: CGAffineTransform = self.colorLayer.affineTransform() transform = CGAffineTransform(rotationAngle: CGFloat.pi/4) self.colorLayer.setAffineTransform(transform) } // 随机颜色 let red: CGFloat = CGFloat(arc4random_uniform(255))/CGFloat(255.0) let green: CGFloat = CGFloat( arc4random_uniform(255))/CGFloat(255.0) let blue: CGFloat = CGFloat(arc4random_uniform(255))/CGFloat(255.0) let color: UIColor = UIColor(red: red, green: green, blue: blue, alpha: 0.3) // 背景色 colorLayer.backgroundColor = color.cgColor // 提交事务 CATransaction.commit() }
#####呈现与模型 当设置CALayer的属性,其实是在定义当前事务结束以后图层如何显示的模型。 这意味着CALayer除了“真实”值(就是你设置的值)以外,必需要知道当前显示在屏幕上的属性值的记录 每一个图层属性的显示值都被存储在一个叫作呈现图层的独立图层当中,他能够经过-presentationLayer方法来访问。 这个呈现图层其实是模型图层的复制,可是它的属性值表明了在任何指定时刻当前外观效果。 呈现图层表明了用户当前看到的图层位置,而不是当前动画结束以后的位置 你能够经过呈现图层的值来获取当前屏幕上真正显示出来的值 使用presentationLayer图层来判断当前图层位置
var colorLayer: CALayer! override func viewDidLoad() { super.viewDidLoad() colorLayer = CALayer() colorLayer.frame = CGRect(x: 50, y: 50, width: 100, height: 100) colorLayer.backgroundColor = UIColor.blue.cgColor self.view.layer.addSublayer(colorLayer) // 使用推动过渡的色值动画 let transition: CATransition = CATransition() transition.type = kCATransitionPush transition.subtype = kCATransitionFromLeft colorLayer.actions = ["backgroundColor":transition] self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapAction(sender:)))) } /** 点击图层自己能够随机改变它的颜色 点击屏幕上的任意位置将会让图层平移到那里 */ func tapAction(sender:UIGestureRecognizer){ let point: CGPoint = sender.location(in: self.view) /* 使用presentationLayer图层来判断当前图层位置 经过对呈现图层调用-hitTest:来判断是否被点击。 若是修改代码让-hitTest:直接做用于colorLayer而不是呈现图层,你会发现当图层移动的时候它并不能正确显示。 这时候你就须要点击图层将要移动到的位置而不是图层自己来响应点击(这就是为何用呈现图层来响应交互的缘由)。 */ if ((colorLayer.presentation()?.hitTest(point)) != nil){ // 点击图层自己能够随机改变它的颜色 // 随机颜色 let red: CGFloat = CGFloat(arc4random_uniform(255))/CGFloat(255.0) let green: CGFloat = CGFloat( arc4random_uniform(255))/CGFloat(255.0) let blue: CGFloat = CGFloat(arc4random_uniform(255))/CGFloat(255.0) let color: UIColor = UIColor(red: red, green: green, blue: blue, alpha: 0.3) // 背景色 colorLayer.backgroundColor = color.cgColor }else{ // 点击屏幕上的任意位置将会让图层平移到那里 CATransaction.begin() CATransaction.setAnimationDuration(2.0) colorLayer.position = point CATransaction.commit() } }
###显示动画CAAnimationDelegate #####属性动画 当更新属性的时候,咱们须要设置一个新的事务,而且禁用图层行为。不然动画会发生两次,一个是由于显式的CABasicAnimation,另外一次是由于隐式动画
动画完成以后修改图层的背景色
// // ViewController.swift // Demo // // Created by joker on 2016/11/9. // Copyright © 2016年 joker. All rights reserved. // import UIKit import GLKit class ViewController: UIViewController { var colorLayer: CALayer! override func viewDidLoad() { super.viewDidLoad() colorLayer = CALayer() colorLayer.frame = CGRect(x: 50, y: 50, width: 100, height: 100) colorLayer.backgroundColor = UIColor.blue.cgColor self.view.layer.addSublayer(colorLayer) // 使用推动过渡的色值动画 let transition: CATransition = CATransition() transition.type = kCATransitionPush transition.subtype = kCATransitionFromLeft colorLayer.actions = ["backgroundColor":transition] self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapAction(sender:)))) } ///动画完成以后修改图层的背景色 func tapAction(sender:UIGestureRecognizer){ // 随机颜色 let red: CGFloat = CGFloat(arc4random_uniform(255))/CGFloat(255.0) let green: CGFloat = CGFloat( arc4random_uniform(255))/CGFloat(255.0) let blue: CGFloat = CGFloat(arc4random_uniform(255))/CGFloat(255.0) let color: UIColor = UIColor(red: red, green: green, blue: blue, alpha: 0.3) let animation: CABasicAnimation = CABasicAnimation() animation.keyPath = "backgroundColor" animation.toValue = color.cgColor animation.delegate = self colorLayer.add(animation, forKey: nil) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } extension ViewController: CAAnimationDelegate { /// 用 CAAnimationDelegate animationDidStop 方法在动画结束以后来更新图层的backgroundColor。 func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { if let baseAnimation = anim as? CABasicAnimation { //设置一个新的事务,而且禁用图层行为 CATransaction.begin() CATransaction.setDisableActions(true) colorLayer.backgroundColor = baseAnimation.toValue as! CGColor CATransaction.commit() } } }
对CAAnimation而言,使用委托模式而不是一个完成块会带来一个问题,就是当你有多个动画的时候,没法在在回调方法中区分。在一个视图控制器中建立动画的时候,一般会用控制器自己做为一个委托,可是全部的动画都会调用同一个回调方法,因此你就须要判断究竟是那个图层的调用。
当使用-addAnimation:forKey:把动画添加到图层,这里有一个到目前为止咱们都设置为nil的key参数。这里的键是-animationForKey:方法找到对应动画的惟一标识符,而当前动画的全部键均可以用animationKeys获取。若是咱们对每一个动画都关联一个惟一的键,就能够对每一个图层循环全部键,而后调用-animationForKey:来比对结果。
像全部的NSObject子类同样,CAAnimation实现了KVC(键-值-编码)协议,因而你能够用-setValue:forKey:和-valueForKey:方法来存取属性。可是CAAnimation有一个不一样的性能:它更像一个NSDictionary,可让你随意设置键值对,即便和你使用的动画类所声明的属性并不匹配。
// // ViewController.swift // Demo // // Created by joker on 2016/11/9. // Copyright © 2016年 joker. All rights reserved. // import UIKit import GLKit class ViewController: UIViewController { var h: UIView! var m: UIView! var s: UIView! var timer: Timer! func set1(){ // 时分秒 指针 h = UIView(frame: CGRect(x: 200, y: 150, width: 5, height: 50)) h.backgroundColor = UIColor.red m = UIView(frame: CGRect(x: 200, y: 130, width: 5, height: 70)) m.backgroundColor = UIColor.green s = UIView(frame: CGRect(x: 200, y: 100, width: 5, height: 100)) s.backgroundColor = UIColor.blue self.view.addSubview(h) self.view.addSubview(m) self.view.addSubview(s) h.layer.anchorPoint = CGPoint(x: 0.5, y: 0.9) m.layer.anchorPoint = CGPoint(x: 0.5, y: 0.9) s.layer.anchorPoint = CGPoint(x: 0.5, y: 0.9) } func set2(){ updateHands(animated: false) // 定时器 timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.tick), userInfo: nil, repeats: true) } func tick(){ updateHands(animated: true) } func updateHands(animated:Bool){ let calendar: Calendar = Calendar(identifier: Calendar.Identifier.gregorian) let components: DateComponents = calendar.dateComponents(in: TimeZone.current, from: Date()) let hoursAngle: CGFloat = CGFloat(components.hour!) / 12 * CGFloat.pi * 2 let minsAngle: CGFloat = CGFloat(components.minute!) / 60 * CGFloat.pi * 2 let secsAngle: CGFloat = CGFloat(components.second!) / 60 * CGFloat.pi * 2 setAngle(angle: hoursAngle, handView: h, animated: animated) setAngle(angle: minsAngle, handView: m, animated: animated) setAngle(angle: secsAngle, handView: s, animated: animated) } func setAngle(angle:CGFloat , handView:UIView, animated:Bool){ let transform: CATransform3D = CATransform3DMakeRotation(angle, 0, 0, 1) if animated { /* 给UIView添加动画, 能够简单地判断动画到底属于哪一个视图, 而后在委托方法中用这个信息正确地更新钟的指针 */ let animation: CABasicAnimation = CABasicAnimation() updateHands(animated: false) animation.keyPath = "transform" animation.toValue = transform animation.duration = 0.5 animation.delegate = self animation.setValue(handView, forKey: "handView") handView.layer.add(animation, forKey: nil) }else{ handView.layer.transform = transform } } override func viewDidLoad() { super.viewDidLoad() set1() set2() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } extension ViewController: CAAnimationDelegate { /* 当真正跑在iOS设备上时,咱们发如今-animationDidStop:finished:委托方法调用以前,指针会迅速返回到原始值 能够用一个fillMode属性来解决这个问题 */ func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { if let baseAnimation = anim as? CABasicAnimation { let v: UIView = anim.value(forKey: "handView") as! UIView v.layer.transform = baseAnimation.toValue as! CATransform3D } } }
#####关键帧动画 显式地给图层添加CABasicAnimation相较于隐式动画而言,只能说费力不讨好。
CAKeyframeAnimation是另外一种UIKit没有暴露出来但功能强大的类。和CABasicAnimation相似,CAKeyframeAnimation一样是CAPropertyAnimation的一个子类,它依然做用于单一的一个属性,可是和CABasicAnimation不同的是,它不限制于设置一个起始和结束的值,而是能够根据一连串随意的值来作动画。
关键帧起源于传动动画,意思是指主导的动画在显著改变发生时重绘当前帧(也就是关键帧),每帧之间剩下的绘制(能够经过关键帧推算出)将由熟练的艺术家来完成。CAKeyframeAnimation也是一样的道理:你提供了显著的帧,而后Core Animation在每帧之间进行插入
设置一个颜色的数组,而后经过关键帧动画播放出来
// // ViewController.swift // Demo // // Created by joker on 2016/11/9. // Copyright © 2016年 joker. All rights reserved. // import UIKit import GLKit class ViewController: UIViewController { var colorLayer: CALayer! override func viewDidLoad() { super.viewDidLoad() colorLayer = CALayer() colorLayer.frame = CGRect(x: 50, y: 50, width: 100, height: 100) colorLayer.backgroundColor = UIColor.blue.cgColor self.view.layer.addSublayer(colorLayer) // 使用推动过渡的色值动画 let transition: CATransition = CATransition() transition.type = kCATransitionPush transition.subtype = kCATransitionFromLeft colorLayer.actions = ["backgroundColor":transition] self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapAction(sender:)))) } func tapAction(sender:UIGestureRecognizer){ /* 注意到序列中开始和结束的颜色都是蓝色, 这是由于CAKeyframeAnimation并不能自动把当前值做为第一帧(就像CABasicAnimation那样把fromValue设为nil)。 动画会在开始的时候忽然跳转到第一帧的值,而后在动画结束的时候忽然恢复到原始的值。 因此为了动画的平滑特性,咱们须要开始和结束的关键帧来匹配当前属性的值。 */ let animation: CAKeyframeAnimation = CAKeyframeAnimation() animation.keyPath = "backgroundColor" animation.duration = 2.0 animation.values = [ UIColor.blue.cgColor, UIColor.red.cgColor, UIColor.green.cgColor, UIColor.black.cgColor, UIColor.blue.cgColor ] colorLayer.add(animation, forKey: nil) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } extension ViewController: CAAnimationDelegate { /// 用 CAAnimationDelegate animationDidStop 方法在动画结束以后来更新图层的backgroundColor。 func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { if let baseAnimation = anim as? CABasicAnimation { //设置一个新的事务,而且禁用图层行为 CATransaction.begin() CATransaction.setDisableActions(true) colorLayer.backgroundColor = baseAnimation.toValue as! CGColor CATransaction.commit() } } }
CAKeyframeAnimation有另外一种方式去指定动画,就是使用CGPath。path属性能够用一种直观的方式,使用Core Graphics函数定义运动序列来绘制动画。 为了建立路径,咱们须要使用一个三次贝塞尔曲线,它是一种使用开始点,结束点和另外两个控制点来定义形状的曲线,能够经过使用一个基于C的Core Graphics绘图指令来建立,不过用UIKit提供的UIBezierPath类会更简单。 咱们此次用CAShapeLayer来在屏幕上绘制曲线,尽管对动画来讲并非必须的,但这会让咱们的动画更加形象。绘制完CGPath以后,咱们用它来建立一个CAKeyframeAnimation,
沿着一个贝塞尔曲线对图层作动画
let v: UIView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100)) v.backgroundColor = UIColor.blue self.view.addSubview(v) let bezierPath: UIBezierPath = UIBezierPath() bezierPath.move(to: CGPoint(x: 150, y: 0)) bezierPath.addCurve(to: CGPoint(x: 150, y: 300), controlPoint1: CGPoint(x: 0, y: 75), controlPoint2: CGPoint(x: 300, y: 225)) let pathLayer: CAShapeLayer = CAShapeLayer() pathLayer.path = bezierPath.cgPath pathLayer.fillColor = UIColor.clear.cgColor pathLayer.strokeColor = UIColor.red.cgColor pathLayer.lineWidth = 3 v.layer.addSublayer(pathLayer) let shipLayer: CALayer = CALayer() shipLayer.frame = CGRect(x: 0, y: 0, width: 50, height: 50) shipLayer.position = CGPoint(x: 0, y: 150) shipLayer.contents = UIImage(named: "bg.jpg")?.cgImage v.layer.addSublayer(shipLayer) let animation: CAKeyframeAnimation = CAKeyframeAnimation() animation.keyPath = "position" animation.duration = 4 /* 给CAKeyFrameAnimation添加了一个rotationMode的属性。 设置它为常量kCAAnimationRotateAuto,图层将会根据曲线的切线自动旋转 */ animation.rotationMode = kCAAnimationRotateAuto animation.path = bezierPath.cgPath shipLayer.add(animation, forKey: nil)
#####虚拟属性 若是想要对一个物体作旋转的动画,那就须要做用于transform属性,由于CALayer没有显式提供角度或者方向之类的属性
let v: UIView = UIView(frame: CGRect(x: 50, y: 50, width: 100, height: 100)) v.backgroundColor = UIColor.blue self.view.addSubview(v) // 动画 let animation: CABasicAnimation = CABasicAnimation() animation.duration = 2 // // 使用toValue // /* // 旋转 180 // 若是咱们把旋转的值从M_PI(180度)调整到2 * M_PI(360度),而后运行程序,会发现这时候bg.jpg彻底不动了 // 这是由于这里的矩阵作了一次360度的旋转,和作了0度是同样的,因此最后的值根本没变。 // */ // animation.toValue = CATransform3DMakeRotation(CGFloat.pi, 0, 0, 1) // animation.keyPath = "transform" // // 使用byValue /* 旋转 M_PI(180度)*2 如今继续使用M_PI,但此次用byValue而不是toValue。 飞船的图片变大了,并无作任何旋转,这是由于变换矩阵不能像角度值那样叠加。 */ animation.byValue = CGFloat.pi*2 /* transform.rotation属性有一个奇怪的问题是它其实并不存在。 这是由于CATransform3D并非一个对象,它其实是一个结构体,也没有符合KVC相关属性, transform.rotation其实是一个CALayer用于处理动画变换的虚拟属性。 用transform.rotation而不是transform作动画的好处以下 不经过关键帧一步旋转多于180度的动画 用相对值而不是绝对值旋转(设置byValue而不是toValue)。 不用建立CATransform3D,而是使用一个简单的数值来指定角度。 不会和transform.position或者transform.scale冲突(一样是使用关键路径来作独立的动画属性)。 */ animation.keyPath = "transform.rotation" let shipLayer: CALayer = CALayer() shipLayer.frame = CGRect(x: 0, y: 0, width: 50, height: 50) shipLayer.position = CGPoint(x: 0, y: 150) shipLayer.contents = UIImage(named: "bg.jpg")?.cgImage v.layer.addSublayer(shipLayer) shipLayer.add(animation, forKey: nil)
不能够直接设置transform.rotation或者transform.scale,他们不能被直接使用。当你对他们作动画时,Core Animation自动地根据经过CAValueFunction来计算的值来更新transform属性 CAValueFunction用于把咱们赋给虚拟的transform.rotation简单浮点值转换成真正的用于摆放图层的CATransform3D矩阵值。你能够经过设置CAPropertyAnimation的valueFunction属性来改变,因而你设置的函数将会覆盖默认的函数。 CAValueFunction看起来彷佛是对那些不能简单相加的属性(例如变换矩阵)作动画的很是有用的机制,但因为CAValueFunction的实现细节是私有的,因此目前不能经过继承它来自定义。你能够经过使用苹果目前已经提供的常量(目前都是和变换矩阵的虚拟属性相关,因此没太多使用场景了,由于这些属性都有了默认的实现方式)。
###动画组 CABasicAnimation和CAKeyframeAnimation仅仅做用于单独的属性,而CAAnimationGroup能够把这些动画组合在一块儿。CAAnimationGroup是另外一个继承于CAAnimation的子类,它添加了一个animations数组的属性,用来组合别的动画。
关键帧动画和调整图层背景色的基础动画组合起来
//贝塞尔曲线 let bezierPath: UIBezierPath = UIBezierPath() bezierPath.move(to: CGPoint(x: 0, y: 150)) bezierPath.addCurve(to: CGPoint(x: 300, y: 150), controlPoint1: CGPoint(x: 75, y: 0), controlPoint2: CGPoint(x: 225, y: 300)) /* CAShapeLayer是一个经过矢量图形而不是bitmap来绘制的图层子类。 你指定诸如颜色和线宽等属性,用CGPath来定义想要绘制的图形, 最后CAShapeLayer就自动渲染出来了 */ let pathLayer: CAShapeLayer = CAShapeLayer() pathLayer.path = bezierPath.cgPath pathLayer.fillColor = UIColor.clear.cgColor pathLayer.strokeColor = UIColor.red.cgColor pathLayer.lineWidth = 3.0 self.view.layer.addSublayer(pathLayer) // 图层(要操做的对象) let colorLayer: CALayer = CALayer() colorLayer.frame = CGRect(x: 0, y: 0, width: 64, height: 64) colorLayer.position = CGPoint(x: 0, y: 150) colorLayer.backgroundColor = UIColor.green.cgColor self.view.layer.addSublayer(colorLayer) /* 关键帧动画 CAKeyframeAnimation是另外一种UIKit没有暴露出来但功能强大的类。 和CABasicAnimation相似,CAKeyframeAnimation一样是CAPropertyAnimation的一个子类, 它依然做用于单一的一个属性, 可是和CABasicAnimation不同的是,它不限制于设置一个起始和结束的值,而是能够根据一连串随意的值来作动画。 */ let animation1: CAKeyframeAnimation = CAKeyframeAnimation() animation1.keyPath = "position" animation1.path = bezierPath.cgPath animation1.rotationMode = kCAAnimationRotateAuto // 显式动画 CABasicAnimation 属性动画 let animation2: CABasicAnimation = CABasicAnimation() animation2.keyPath = "backgroundColor" animation2.toValue = UIColor.red.cgColor /* CABasicAnimation和CAKeyframeAnimation仅仅做用于单独的属性, 而CAAnimationGroup能够把这些动画组合在一块儿。 CAAnimationGroup是另外一个继承于CAAnimation的子类, 它添加了一个animations数组的属性,用来组合别的动画。 */ let groupAnimation: CAAnimationGroup = CAAnimationGroup() groupAnimation.animations = [animation1,animation2] groupAnimation.duration = 4.0 colorLayer.add(groupAnimation, forKey: nil)
###过渡 过渡并不像属性动画那样平滑地在两个值之间作动画,而是影响到整个图层的变化。过渡动画首先展现以前的图层外观,而后经过一个交换过渡到新的外观。
对图层contents图片作的改动都会自动附上淡入淡出的动画
使用CAAnimation的子类CATransition建立一个过渡动画,CATransition有一个type和subtype来标识变换效果
经过subtype来控制它们的方向
type来控制过渡效果
var imgView: UIImageView! var imgs: [UIImage]! override func viewDidLoad() { super.viewDidLoad() imgs = [ UIImage(named: "bg.jpg")!, UIImage(named: "star.png")! ] imgView = UIImageView(frame: CGRect(x: 50, y: 50, width: 100, height: 100)) imgView.image = imgs[0] self.view.addSubview(imgView) self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapAction(sender:)))) } func tapAction(sender:UIGestureRecognizer){ // 1 CATransition过渡动 //过渡动画 let transition: CATransition = CATransition() //把原始的图层滑动出去来显示新的外观 transition.type = kCATransitionReveal //方向 transition.subtype = kCATransitionFromBottom imgView.layer.add(transition, forKey: nil) let img = imgView.image! var index: Int = imgs.index(of: img)! index = (index + 1) % imgs.count imgView.image = imgs[index] // 2 UIView过渡 // 立体翻转 UIView.transition( with: imgView, duration: 2.0, options: UIViewAnimationOptions.transitionFlipFromLeft, animations: { let img = self.imgView.image! var index: Int = self.imgs.index(of: img)! index = (index + 1) % self.imgs.count self.imgView.image = self.imgs[index] }, completion: { (Bool) in }) }
过渡动画作基础的原则就是对原始的图层外观截图,而后添加一段动画,平滑过渡到图层改变以后那个截图的效果。若是咱们知道如何对图层截图,咱们就可使用属性动画来代替CATransition或者是UIKit的过渡方法来实现动画。 事实证实,对图层作截图仍是很简单的。CALayer有一个-renderInContext:方法,能够经过把它绘制到Core Graphics的上下文中捕获当前内容的图片,而后在另外的视图中显示出来。若是咱们把这个截屏视图置于原始视图之上,就能够遮住真实视图的全部变化,因而从新建立了一个简单的过渡效果。
var imgView: UIImageView! override func viewDidLoad() { super.viewDidLoad() imgView = UIImageView(frame: CGRect(x: 50, y: 50, width: 100, height: 100)) imgView.image = UIImage(named: "bg.jpg") self.view.addSubview(imgView) self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapAction(sender:)))) } func tapAction(sender:UIGestureRecognizer){ // 用renderInContext:建立自定义 缩小,并 旋转过渡效果 /* CALayer有一个-renderInContext:方法, 能够经过把它绘制到Core Graphics的上下文中捕获当前内容的图片, 而后在另外的视图中显示出来。 */ UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, true, 0.0) self.view.layer.render(in: UIGraphicsGetCurrentContext()!) let coverImg: UIImage = UIGraphicsGetImageFromCurrentImageContext()! let coverView: UIView = UIImageView(image: coverImg) coverView.frame = self.view.bounds self.view.addSubview(coverView) // 随机颜色 let red: CGFloat = CGFloat(arc4random_uniform(255))/CGFloat(255.0) let green: CGFloat = CGFloat( arc4random_uniform(255))/CGFloat(255.0) let blue: CGFloat = CGFloat(arc4random_uniform(255))/CGFloat(255.0) let color: UIColor = UIColor(red: red, green: green, blue: blue, alpha: 0.3) self.view.backgroundColor = color UIView.animate( withDuration: 1.0, animations: { // 缩小,并 旋转 动画 var transform: CGAffineTransform = CGAffineTransform(scaleX: 0.01, y: 0.01) transform = transform.rotated(by: CGFloat.pi/4) coverView.transform = transform coverView.alpha=0 }, completion: {(b:Bool) in coverView.removeFromSuperview() }) }
###在动画过程当中取消动画 用-addAnimation:forKey:方法中的key参数来在添加动画以后检索一个动画,但并不支持在动画运行过程当中修改动画,因此这个方法主要用来检测动画的属性,或者判断它是否被添加到当前图层中。 为了终止一个指定的动画,你能够把它从图层移除掉 removeAnimation ,动画一旦被移除,图层的外观就马上更新到当前的模型图层的值。
开始和中止一个动画
// // ViewController.swift // Demo // // Created by joker on 2016/11/9. // Copyright © 2016年 joker. All rights reserved. // import UIKit import GLKit class ViewController: UIViewController { var shipLayer: CALayer! var isAnimation: Bool = true override func viewDidLoad() { super.viewDidLoad() shipLayer = CALayer() shipLayer.frame = CGRect(x: 100, y: 100, width: 50, height: 50) shipLayer.position = CGPoint(x: 100, y: 100) shipLayer.contents = UIImage(named: "bg.jpg")?.cgImage self.view.layer.addSublayer(shipLayer) self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapAction(sender:)))) } func tapAction(sender:UIGestureRecognizer){ if isAnimation{ // 开始动画 let animation: CABasicAnimation = CABasicAnimation() animation.keyPath = "transform.rotation" animation.duration = 2.0 animation.byValue = CGFloat.pi*2 animation.delegate = self shipLayer.add(animation, forKey: "rotateAnimation") }else{ // 结束动画 shipLayer.removeAnimation(forKey: "rotateAnimation") } isAnimation = !isAnimation } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } extension ViewController: CAAnimationDelegate{ func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { isAnimation = true print("the animation stop \(flag)") } }