iOS核心動畫進階技巧之核心動畫(三),ios核心動畫技巧

來源:互聯網
上載者:User

iOS核心動畫進階技巧之核心動畫(三),ios核心動畫技巧

iOS核心動畫進階技巧之CALayer(一)

iOS核心動畫進階技巧之圖層變換和專用圖層(二)
iOS核心動畫進階技巧之核心動畫(三)
iOS核心動畫進階技巧之效能(四)
iOS核心動畫進階技巧之動畫總結(五)

 隱式動畫

  隱式動畫主要作用於CALayer的可動畫屬性上面,UIView對應的layer是不可以的,只要你改變屬性的值,它不是突兀的直接改變過去,而是一個有一個動畫的過程,這個時間等屬性你可以通過事務(CATransaction)來控制,如果你不自己提供一個事務,它的預設時間是0.25秒,當然這個可動畫屬性是需要觸發的,如果你一上來就設定一個值,可能看不到動畫效果.

 1 redLayer = CALayer() 2 redLayer.backgroundColor = UIColor.redColor().CGColor 3 redLayer.frame = CGRectMake(50, 100, 100, 100) 4 self.view.layer.addSublayer(redLayer) 5  6 NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: NSSelectorFromString("animate"), userInfo: nil, repeats: false) 7  8 func  animate() { 9     CATransaction.begin()10     CATransaction.setAnimationDuration(12)11     12     var redC =  CGFloat(arc4random() % 256 ) / 255.013     var greenC = CGFloat(arc4random() % 256 ) / 255.014     var blueC = CGFloat(arc4random() % 256 ) / 255.015     16     self.redLayer.backgroundColor = UIColor(red: redC, green: greenC, blue: blueC, alpha: 1).CGColor17     18     CATransaction.commit()19 }

  在上面這個transaction中加上一個完成塊可以使它在在動畫完成的時候做一個0.25秒的預設動畫,demo中有兩種動畫方式都可以(2d和3d)

 1 CATransaction.begin() 2 CATransaction.setAnimationDuration(3) 3 CATransaction.setCompletionBlock { () -> Void in 4 /*3d動畫 5 var transform = self.redLayer.transform 6 transform = CATransform3DRotate(transform, CGFloat(M_PI_4), 0, 0, 1) 7 self.redLayer.transform = transform 8 */ 9 //這個是2d的動畫10 var transform = self.redLayer.affineTransform()11 transform = CGAffineTransformRotate(transform, CGFloat(M_PI_4))12 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沒什麼反應

1 var transition = CATransition()2 transition.type = kCATransitionPush//設定出現方式,預設為fade3 transition.subtype = kCATransitionFromLeft//設定出現方向4 redLayer.actions = ["backgroundColor":transition]

  layer能做隱式動畫,但是你擷取可動畫的屬性它的值還是最後設定的值,那是因為它並不是一個layer產生的動畫,它有modelLayer一般是返回layer本身,它還有一個presenttationLayer呈現圖層,我們設定的值是給modelLayer的,而做動畫的是presentationLayer.有兩種情況你可能需要用到呈現圖層,一種是你需要獲得動畫過程中layer的位置,另一種是你需要在動畫過程中響應使用者互動.下面這個demo效果是如果你點擊到了方塊則變顏色,沒點到則方塊移動到你點擊的位置

 1 redLayer = CALayer() 2 redLayer.backgroundColor = UIColor.redColor().CGColor 3 redLayer.frame = CGRectMake(50, 100, 100, 100) 4 self.view.layer.addSublayer(redLayer) 5  6 override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) { 7     var point = ( touches as NSSet ) .anyObject()?.locationInView(self.view) 8      9     if (self.redLayer.presentationLayer().hitTest(point!) != nil) {10         var redC =  CGFloat(arc4random() % 256 ) / 255.011         var greenC = CGFloat(arc4random() % 256 ) / 255.012         var blueC = CGFloat(arc4random() % 256 ) / 255.013         14         self.redLayer.backgroundColor = UIColor(red: redC, green: greenC, blue: blueC, alpha: 1).CGColor15     }else {16         CATransaction.begin()17         CATransaction.setAnimationDuration(2.0)18         self.redLayer.position = point!19         CATransaction.commit()20     }21 }

   顯式動畫

CABasicAnimation(屬性動畫) 

  CABasicAnimation動畫和隱式動畫類似,它可以設定起始和結束值和delegate,下面這個例子和上面的隱式動畫類似

1 redLayer = CALayer() 2 redLayer.backgroundColor = UIColor.redColor().CGColor 3 redLayer.frame = CGRectMake(50, 100, 100, 100) 4 self.view.layer.addSublayer(redLayer) 5 6 NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: NSSelectorFromString("animate"), userInfo: nil, repeats: false) 7 } 8 9 func animate () {10 var redC = CGFloat(arc4random() % 256 ) / 255.011 var greenC = CGFloat(arc4random() % 256 ) / 255.012 var blueC = CGFloat(arc4random() % 256 ) / 255.013 var color = UIColor(red: redC, green: greenC, blue: blueC, alpha: 1).CGColor14 15 var animate = CABasicAnimation()16 animate.duration = 8.017 animate.keyPath = "backgroundColor"18 animate.toValue = color19 animate.delegate = self20 self.redLayer .addAnimation(animate, forKey: nil)21 }22 23 override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {24 CATransaction.begin()25 26 CATransaction.setDisableActions(true)27 var animate = anim as! CABasicAnimation28 self.redLayer.backgroundColor = animate.toValue as! CGColorRef29 CATransaction.commit()30 }View Code

  注意在代碼中,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的動畫,你看是多簡單.

 1    NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: NSSelectorFromString("animate"), userInfo: nil, repeats: false) 2      3     self.img = UIImageView(image: UIImage(named: "111.png")) 4     self.view.addSubview(self.img) 5 } 6  7 func animate () { 8     var transition = CATransition() 9     transition.type = kCATransitionFade10     self.img.layer .addAnimation(transition, forKey: nil)11     12     self.img.image = UIImage(named: "222.png")13 }

 

  對於CAtransition來說,在自己建立的layer中,它是預設加上的,而在view關聯的屠城,它是被禁用的,畢竟它還是得提供你一種簡單的正常的設定屬性的方法,它是對整個圖層樹都有效,如果添加了transition會跟它的子圖層都加上整個效果,比如跟tabbar切換的漸層效果(大多數VC切換都可以在代理方法或其它方法中寫動畫)

 1     var root = UITabBarController() 2     root.viewControllers = [ViewController(),OneViewController()] 3     root.delegate = self 4     self.rootVC = root 5    6     self.window?.rootViewController = root 7     self.window?.makeKeyAndVisible() 8      9     return true10 }11  func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {12     var transition = CATransition()13     transition.type = kCATransitionFade14     transition.duration = 315     self.rootVC.view.layer.addAnimation(transition, forKey: nil)16 }

  UiView提供了transitionFromView的方法,如果只跟一個view做動畫直接可以用它和加上transition動畫的效果是一樣的,但是一般它對子圖層不起作用,在動畫啟動並執行過程中可以remove掉動畫,一個按鈕添加動畫,一個按鈕控制刪除動畫,而動畫是使用的byValue,所以效果就是看起來是暫停.

  下面的代碼做的是暫停動畫的功能,在暫停時候更新model樹為presentlayer的值.

1 redLayer = CALayer() 2 redLayer.backgroundColor = UIColor.redColor().CGColor 3 redLayer.frame = CGRectMake(50, 100, 100, 100) 4 self.view.layer.addSublayer(redLayer) 5 6 var beginBtn = UIButton(frame: CGRectMake(50, 300, 100, 30)) 7 beginBtn.setTitle("開始", forState: UIControlState.Normal) 8 beginBtn.addTarget(self, action: "begin", forControlEvents: UIControlEvents.TouchUpInside) 9 self.view.addSubview(beginBtn)10 11 var stopBtn = UIButton(frame: CGRectMake(200, 300, 100, 30))12 stopBtn.setTitle("暫停", forState: UIControlState.Normal)13 stopBtn.addTarget(self, action: "stop", forControlEvents: UIControlEvents.TouchUpInside)14 self.view.addSubview(stopBtn)15 }16 17 func begin() {18 var animate = CABasicAnimation()19 animate.keyPath = "transform.rotation"20 animate.duration = 2.021 animate.byValue = CGFloat(M_PI / 4)22 animate.delegate = self23 // animate.fillMode = kCAFillModeForwards24 // animate.removedOnCompletion = false25 self.redLayer .addAnimation(animate, forKey: "animate")26 }27 func stop() {28 self.redLayer.transform = self.redLayer.presentationLayer().transform29 self.redLayer.removeAnimationForKey("animate")30 }View Code

圖層時間

  beginTime/speed/timeoffset 三個動畫屬性都是相對概念,分別表示對應duration的開始時間,動畫的速度(會改變動畫結束的時間),讓動畫瞬間快進到某一點.下面是讓動畫暫停第二種方法.

1 redLayer = CALayer() 2 redLayer.backgroundColor = UIColor.redColor().CGColor 3 redLayer.frame = CGRectMake(50, 100, 100, 100) 4 self.view.layer.addSublayer(redLayer) 5 6 var beginBtn = UIButton(frame: CGRectMake(50, 300, 100, 30)) 7 beginBtn.setTitle("開始", forState: UIControlState.Normal) 8 beginBtn.addTarget(self, action: "resumeLayer", forControlEvents: UIControlEvents.TouchUpInside) 9 self.view.addSubview(beginBtn)10 11 var stopBtn = UIButton(frame: CGRectMake(200, 300, 100, 30))12 stopBtn.setTitle("暫停", forState: UIControlState.Normal)13 stopBtn.addTarget(self, action: "pauseLayer", forControlEvents: UIControlEvents.TouchUpInside)14 self.view.addSubview(stopBtn)15 16 self.pauseTime = 0.017 self.startAnimate()18 }19 20 func startAnimate () {21 var animate = CABasicAnimation()22 animate.keyPath = "transform.rotation"23 animate.duration = 20.024 animate.byValue = CGFloat(M_PI * 10)25 animate.delegate = self26 self.redLayer .addAnimation(animate, forKey: "animate")27 }28 29 func pauseLayer () {30 //擷取當前動畫的時間31 self.pauseTime = self.redLayer.convertTime(CACurrentMediaTime(), fromLayer: nil)32 //停止運動33 self.redLayer.speed = 0.034 //設定它呆在目前的狀態不變,不然因為speed為0,layer變回了最初動畫的值35 self.redLayer.timeOffset = self.pauseTime36 }37 38 func resumeLayer () {39 //擷取暫停開始的時間40 var pausedTime = self.pauseTime41 //設定速度timeOffset等為正常值42 self.redLayer.speed = 1.043 self.redLayer.timeOffset = 0.044 self.redLayer.beginTime = 0.045 46 var timeSincePause = self.redLayer.convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime47 //設定開始時間為初始和現在的時間差48 self.redLayer.beginTime = timeSincePause49 }View Code

  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。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.