21- 《A GUIDE TO IOS ANIMATION 2.0》(楊騎滔)三章.CoreAnimation-學習筆記: 源碼分析和盲點記錄
第一個案例: Twitter 的啟動動畫
這個案例之前有看過.當時覺得很有意思. 今天看了楊騎滔給出的實現(xiàn)思路之后,才發(fā)現(xiàn)實現(xiàn)代碼不難,難的還是你的實現(xiàn)的思路. 我們仔細觀察,書中的啟動動畫,開始的頁面是從程序啟動之后一直到進入根控制器都是同一個,而且明顯,要比我們平時的啟動界面停留的時間要長.這個的實現(xiàn)思路和我們看到的很多主流APP進入之后,會有一個廣告界面,還有倒計時.這個肯定是系統(tǒng)自帶的LaunchScreen.xib啟動完成之后,在顯示根控制器之前穿插的. 只不過,廣告和啟動是不同的頁面,我們這里是需要兩個樣式一模一樣的頁面.
原理,我就不贅述了.我想看了書的人和看了源碼的人早已經(jīng)了然于胸. 現(xiàn)在只是記錄下自己的知識的盲點.
UIWindow的兩個方法
[self.window makeKeyAndVisible]; 和 self.window.rootViewController的具體作用:
-------- 占位行 ------------
mask屬性
“在開始之前,我們先了解一下 Layer 的 mask 屬性。
@property(strong) CALayer *mask;
可以發(fā)現(xiàn) mask 也是一個 CALayer”
--- 摘錄來自: 楊騎滔(KittenYang). “A GUIDE TO IOS ANIMATION”。 iBooks.
//logo mask 添加一個遮罩,圖片透明和不透明的地方反轉(zhuǎn)
CALayer *maskLayer = [CALayer layer];
maskLayer.contents = (id)[UIImage imageNamed:@"1"].CGImage; //必須是CGImageRef
maskLayer.position = navc.view.center;
maskLayer.bounds = CGRectMake(0, 0, 60, 60);
navc.view.layer.mask = maskLayer; //為mask(CALayer)屬性賦值之后,露出window是黑色的,必須通過改變self.window.background的顏色
CoreAnimation - CAKeyframeAnimation
---- 以下均為原文,摘錄來自: 楊騎滔(KittenYang). “A GUIDE TO IOS ANIMATION”。 iBooks.
- 顧名思義,CAKeyframeAnimation 就相當于 Flash 里的關鍵幀動畫”
- CAKeyframeAnimation 中我們通過 keyPath 就可以指定動畫的類型?!?/li>
- 然后,我們把每個關鍵幀的對應參數(shù)賦值給 CAKeyframeAnimation 的 values 屬性。代碼中,我設置了3個關鍵幀,transformAnimation.values = xxx
- 設置對應的時間點 transformAnimation.keyTimes = xxx
- 但是我們還要注意。當你給一個 CALayer 添加動畫的時候,動畫其實并沒有改變這個 layer 的實際屬性。取而代之的,系統(tǒng)會創(chuàng)建一個原始 layer 的拷貝。在文檔中,蘋果稱這個原始 layer 為 Model Layer ,而這個復制的 layer 則被稱為 Presentation Layer 。 Presentation Layer 的屬性會隨著動畫的進度實時改變,而 Model Layer 中對應的屬性則并不會改變。所以如果你想要獲取動畫中每個時刻的狀態(tài),請使用 layer 的 func presentationLayer() -> AnyObject!
- 為了讓動畫在結束之后不突然回到了初始狀態(tài), 引出 removedOnCompletion 和 fillMode 了。
removedOnCompletion 的官方解釋是:
/* When true, the animation is removed from the render tree once its- active duration has passed. Defaults to YES. */
也就是默認情況下系統(tǒng)會在 duration 時間后自動移除這個 CAKeyframeAnimation。當 remove 了某個動畫,那么系統(tǒng)就會自動銷毀這個 layer 的 Presentation Layer ,只留下 Model Layer ”
- 你需要先把 removedOnCompletion 設置為 false ,然后設置 fillMode 為kCAFillModeForwards” (TIPS: 對于 CAAnimation ,你需要將其 removedOnCompletion 設置為 false 才行,要不然 fillMode 將不起作用?!?。但設置 removedOnCompletion 和 fillMode 不是正確的方式(這個之后再詳細說明)。
- 所以一個好動畫離不開一堆好參數(shù)。
第二個案例: 圓圈遮罩的轉(zhuǎn)場動畫
這個案例講的是轉(zhuǎn)場動畫,這個Demo的效果可以看格瓦拉選中一個條目之后,
關于轉(zhuǎn)場動畫
“iOS7 開始蘋果推出了自定義轉(zhuǎn)場的 API 。從此,任何可以用 CoreAnimation 實現(xiàn)的動畫,都可以出現(xiàn)在兩個 ViewController 的切換之間?!?/p>
“蘋果在 UINavigationControllerDelegate 和 UIViewControllerTransitioningDelegate 中給出了幾個協(xié)議方法,通過返回類型就可以很清楚地知道各自的具體作用。你只需要重載它們,然后 return 一個動畫的實例對象,一切都搞定了。使用準則就是:UINavigationController pushViewController 時重載 UINavigationControllerDelegate 的方法;UIViewController presentViewController 時重載 UIViewControllerTransitioningDelegate 的方法。”
——摘錄來自: 楊騎滔(KittenYang). “A GUIDE TO IOS ANIMATION”。 iBooks.
實現(xiàn)步驟
1.根據(jù)第一小節(jié)中提到的關于轉(zhuǎn)場動畫的解釋,是說我們在當天起始跳轉(zhuǎn)控制器中實現(xiàn)對應的
push或者present對應的代理方法.
2.創(chuàng)建繼承自 NSObject 并且聲明 UIViewControllerAnimatedTransitioning 的的動畫類
3.重載 UIViewControllerAnimatedTransitioning 中的協(xié)議方法。
就這三步. 具體的轉(zhuǎn)場效果,在第3步中自行定義,你寫成啥樣的,他就按照給你樣轉(zhuǎn)
案例分析
再回到我們這個案例.這個效果,其實是兩個有形狀的UIBezierPath的貝塞爾曲線對象的path值,分別作為CABasicAnimation對象的起始值到終點值. 然后系統(tǒng)內(nèi)部發(fā)生的變化. 參看下邊代碼:
maskLayerAnimation.fromValue = (__bridge id)(maskStartBP.CGPath);
maskLayerAnimation.toValue = (__bridge id)((maskFinalBP.CGPath));
楊騎滔在animateTransition:方法中,首先獲取到跳轉(zhuǎn)和跳轉(zhuǎn)到的控制器,然后把這兩個控制器的view添加到transitionContext.containerView中,接著創(chuàng)建兩個圓形的UIBezierPath對象,最終的轉(zhuǎn)成效果類似我們案例一.實現(xiàn)代碼也有類似的地方.
其中,第一個需要注意的地方是:觸發(fā)點的判斷,判斷在哪個象限,然后得出出發(fā)點的坐標,如果你對象限有點遺忘的話,你可以看下邊這幅圖,馬上就想起來了:

http://blog.csdn.net/ys410900345/article/details/42924827
/** timingFunction
*
* 用于變化起點和終點之間的插值計算,形象點說它決定了動畫運行的節(jié)奏,比如是均勻變化(相同時間變化量相同)還是
* 先快后慢,先慢后快還是先慢再快再慢.
*
* 動畫的開始與結束的快慢,有五個預置分別為(下同):
* kCAMediaTimingFunctionLinear 線性,即勻速
* kCAMediaTimingFunctionEaseIn 先慢后快
* kCAMediaTimingFunctionEaseOut 先快后慢
* kCAMediaTimingFunctionEaseInEaseOut 先慢后快再慢
* kCAMediaTimingFunctionDefault 實際效果是動畫中間比較快.
*/