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)") } }