屬性動畫做用於圖層的某一個單一的屬性, 並指定它的目標值, 或者一連串要作動畫的值. 屬性動畫分爲基礎動畫
和關鍵幀動畫
.數組
動畫就是一段時間內發生的改變, 最簡單的形式就是從一個值改變到另外一個值, 這也是CABaseAnimation
的最主要功能. CABaseAnimation是CAPropertyAnimation的一個子類, 而CAPropertyAnimation的父類是CAAnimation, CAAnimation
同時也是CoreAnimation全部動畫類型的抽象基類. 做爲抽象類, 事實上CAAnimation並無完成太多工做.函數
CAPropertyAnimation經過制定動畫的keyPath做用於一個單一屬性, CAAnimation一般做用於一個指定的CALayer, 因而這裏的keyPath指的是一個圖層的路徑了. 實際上它是關鍵路徑而不只僅是一個屬性的名稱, 就是說, 動畫不只僅能夠做用於圖層自己的屬性, 並且還包含了它的子成員的屬性, 甚至包含虛擬屬性.工具
CABaseAnimation繼承自CAPropertyAnimation, 並添加了以下三個屬性:性能
// 動畫開始以前屬性的值 @property(nullable, strong) id fromValue; // 動畫結束以後屬性的值 @property(nullable, strong) id toValue; // 動畫執行過程當中, 改變的值 @property(nullable, strong) id byValue;
fromValue, toValue, byValue能夠用多種不一樣的方式進行組合, 可是爲了防止衝突, 不能一次性指定三個值. 須要注意的是, 若是是圖片必須轉換成CGImageRef類型橋接成id類型; 若是是顏色也必須轉換成CGColorRef類型橋接成id類型 (__bridge id)
.動畫
一個簡單的改變背景顏色的基礎動畫(實際上若是是單獨的layer, 用隱式動畫比較好):url
// 基礎動畫, 改變顏色 - (void)changeColor { UIView *view = [[UIView alloc] initWithFrame:CGRectMake(10, 70, 80, 80)]; [self.view addSubview:view]; view.backgroundColor = [UIColor redColor]; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"]; animation.toValue = (__bridge id)[UIColor purpleColor].CGColor; animation.duration = 10; [view.layer addAnimation:animation forKey:nil]; }
如上代碼, 存在問題是, 當動畫結束以後圖層顏色立刻變回原來的顏色了, 緣由是作動畫時候的圖層是呈現層, 動畫結束以後呈現層被移除, 因此顏色又變回原來的顏色了. 一種解決方案以下:代理
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(10, 70, 80, 80)]; [self.view addSubview:view]; view.backgroundColor = [UIColor redColor]; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"]; animation.fromValue = (__bridge id) view.layer.backgroundColor; animation.toValue = (__bridge id)[UIColor purpleColor].CGColor; animation.duration = 3; [view.layer addAnimation:animation forKey:nil]; view.backgroundColor = [UIColor purpleColor];
第七章的隱式動畫中, 能夠用CATransaction完成塊中檢測動畫的完成, 可是在顯式動畫中顯然是不可能的, 由於這裏的動畫和事務並無鳥關聯.code
在CAAnimationDelegate的代理方法中, 咱們能夠獲取動畫的開始和結束.orm
以下:視頻
/* Called when the animation begins its active duration. */ - (void)animationDidStart:(CAAnimation *)anim { NSLog(@"animation start"); } /* Called when the animation either completes its active duration or * is removed from the object it is attached to (i.e. the layer). 'flag' * is true if the animation reached the end of its active duration * without being removed. */ - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { NSLog(@"animation finish"); }
咱們不能經過隱式動畫來實現UIView的實例, 由於全部圖層的隱式動畫都被禁用了. 咱們能夠經過簡單的UIView動畫實現簡單的動畫效果, 可是若是想要更好的控制動畫時間, 使用顯式動畫比較好.
動畫的代理方法中, 動畫自己會做爲一個參數傳入委託方法中, 可是須要注意: 委託傳入的動畫參數是原始值的一個深拷貝, 而不是同一個值.
事實上, 在項目中咱們用來惟一標識一個動畫的方法是使用key值, 通常咱們經過 -setValue:forKey:
和 -valueForKey:
這兩個方法來存取. 可是CAAnimation有一個不提歐寧的性能, 它更像一個NSDictionary, 計時你使用動畫類型所聲明的屬性並不匹配, 你也能夠隨意的使用鍵值對.
CAKeyframeAnimation是另外一種UIKit沒有暴露出來但功能強大的類. 它也是CAPropertyAnimation的一個子類, 也做用於單一的一個屬性, 可是它能夠根據已連串隨意的值來作動畫.
關鍵幀起源於傳動動畫, 意思是指主導動畫在顯著改變發生時重繪當前幀(也就是關鍵幀), 沒幀之間剩下的繪製經過關鍵幀推算出來. CAKeyFrameAnimation也是一樣的道理, 你提供了顯著幀以後, CoreAnimation在每幀之間進行插入.
關鍵幀動畫, 能夠提供一個數組, 數組中包含一組相關屬性的值, 而後CoreAnimation會將這些值以動畫的形式展現出來, 兩個值之間的值CoreAnimation會計算出來, 可是, 關鍵幀動畫開始執行時候會直接跳轉到第一幀的值, 而並不會將當前的值平滑過渡到第一幀, 這時候, 咱們通常須要將第一幀設置爲當前顏色值相同的值. 一樣的最後一幀也要和最終的顏色想等, 否則會有跳躍.
// 2. 關鍵幀動畫, 使用顏色數組改變顏色 - (void)keyFrameAnimationChangeColor { CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"backgroundColor"]; keyAnimation.duration = 7; keyAnimation.values = @[(__bridge id)self.colorView.backgroundColor.CGColor, (__bridge id)[UIColor blueColor].CGColor, (__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor blackColor].CGColor, (__bridge id)[UIColor purpleColor].CGColor, (__bridge id)[UIColor cyanColor].CGColor, (__bridge id)[UIColor yellowColor].CGColor]; [self.colorView.layer addAnimation:keyAnimation forKey:nil]; // 配置完動畫後, 更改view顏色, 避免動畫結束以後顏色忽然跳躍式變化回原來顏色. self.colorView.layer.backgroundColor = [UIColor yellowColor].CGColor; }
通常來講, 經過數組描述動畫的運動並不直觀. CAKeyFrameAnimation能夠用另外一種方式指定動畫 CGPath
. path屬性能夠用一種直觀的方式, 使用CoreGraphics函數定義運動序列來繪製動畫路徑.
// 3. 關鍵幀動畫, - (void)pathAnimation { // 使用貝塞爾畫曲線 UIBezierPath *bPath = [UIBezierPath bezierPath]; [bPath moveToPoint:CGPointMake(30, 250)]; [bPath addCurveToPoint:CGPointMake(350, 250) controlPoint1:CGPointMake(100, 150) controlPoint2:CGPointMake(250, 400)]; // 將曲線添加到shapeLayer上面 CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.path = bPath.CGPath; shapeLayer.lineWidth = 0.5; shapeLayer.strokeColor = [UIColor redColor].CGColor; shapeLayer.fillColor = [UIColor clearColor].CGColor; [self.view.layer addSublayer:shapeLayer]; //建立飛機控件 UIImageView *plane = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; plane.center = CGPointMake(30, 150); plane.backgroundColor = [UIColor blueColor]; [self.view addSubview:plane]; // 建立path動畫 CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; keyAnimation.path = bPath.CGPath; keyAnimation.duration = 3.f; // 設置圖形方向跟隨曲線切面方向自動作旋轉動做. keyAnimation.rotationMode = kCAAnimationRotateAuto; [plane.layer addAnimation:keyAnimation forKey:nil]; }
**效果: **
屬性動畫針對的是一個鍵, 也就意味着能夠對子屬性甚至虛擬屬性作動畫. 例如, 若是須要用到旋轉則須要使用 transform
屬性, 可是使用transform直接旋轉2*M_PI, 實際上不會作任何變化, 使用起來仍是很是不方便的, 這時候可使用transform的虛擬屬性. transform.rotation
, 建立CABaseAnimation動畫, 設置keypath是 trabsfirm.rotation
, 設置byValue, 就能夠直接對圖層進行旋轉操做, 比使用 CATransform3D方便的多.
好處以下:
transform.rotation
屬性有個奇特的問題是, 它並不存在. 由於CATransform3D並非一個對象, 它其實是一個結構體, 也沒有符合KVC相關屬性, transform.rotation其實是一個CALayer用於處理動畫變換的虛擬屬性.
你不能夠直接設置transform.rotation
或者transform.scale
, 他們不能被直接使用. 當你對他們作動畫時候, CoreAnimation自動的根據經過CAValueFunction來計算的值來更新transform屬性.
CAValueFunction用於咱們賦值給虛擬的transform.rotation
簡單浮點值轉換成真正的用於擺放圖層的CATransfor3D矩陣值. 你能夠經過CAPropertyAnimation的valueFunction屬性來改變, 因而你設置的函數將會覆蓋默認的函數.
CAValueFunction看起來彷佛是對那些不能簡單相加的屬性(例如變換矩陣)作動畫的很是有用的機制,但因爲CAValueFunction的實現細節是私有的,因此目前不能經過繼承它來自定義。你能夠經過使用蘋果目前已近提供的常量(目前都是和變換矩陣的虛擬屬性相關,因此沒太多使用場景了,由於這些屬性都有了默認的實現方式)。
經過虛擬屬性這一小節, 應該弄明白, CAPropertyAnimation的valueFunction的做用是什麼.
CABaseAnimation的CAKeyframeAnimation僅僅做用於單獨的屬性, 而CAAnimationGroup能夠把這些動畫組合在一塊兒. CAAnimationGroup是另外一個繼承自CAAnimation的子類, 它添加了一個animations數組的屬性, 用來組合別的動畫.
動畫組用起來也是巨簡單. 建立對象, 而後將其它動畫放到animations數組中便可.
// 建立path動畫 CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; keyAnimation.path = bPath.CGPath; keyAnimation.duration = 3.f; keyAnimation.rotationMode = kCAAnimationRotateAuto; // 改變顏色的動畫 CAKeyframeAnimation *colorAnimation = [CAKeyframeAnimation animationWithKeyPath:@"backgroundColor"]; colorAnimation.duration = 7; colorAnimation.values = @[(__bridge id)self.colorView.backgroundColor.CGColor, (__bridge id)[UIColor blueColor].CGColor, (__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor blackColor].CGColor, (__bridge id)[UIColor purpleColor].CGColor, (__bridge id)[UIColor cyanColor].CGColor, (__bridge id)[UIColor yellowColor].CGColor]; // animationGroup 代碼. CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.duration = 7; animationGroup.animations = @[keyAnimation, colorAnimation]; [plane.layer addAnimation:animationGroup forKey:nil];
對iOS應用程序來講, 屬性動畫只對圖層的可動動畫屬性起做用, 可是要改版夜歌不能動畫屬性(好比圖片), 或者從層級關係中添加或者移除圖層, 屬性動畫將不起做用.
因而就有了過分動畫的概念. 過分動畫並不像屬性動畫那亞航平滑的在兩個值之間作作動畫, 而是影響到整個圖層的變化. 過分動畫首先須要展現以前的圖層外觀, 而後經過一個交換過分到新的外觀. 建立過分動畫咱們使用的是 CATransition
, 一樣是另外一個CAAnimation子類, 和別的子類不一樣, CATransform有一個type和subtype來標識變換效果. type是NSString類型值:
kCATransitionFade //淡入淡出 kCATransitionMoveIn //見名知意... kCATransitionPush kCATransitionReveal
subtype用來控制動畫移動的方向:
kCATransitionFade kCATransitionMoveIn kCATransitionPush kCATransitionReveal
當設置CALayer的contents屬性的時候, CATransition的確是默認的行爲. 可是對於與視圖關聯的圖層, 或者是其餘隱式動畫的欣慰, 這個特性依然是被禁用的, 可是對於本身建立的圖層, 這意味着對圖層contents圖片作的改動都會自動附上淡入淡出的動畫.
CATransition並不做用於指定的圖層屬性, 就是說, 能夠在不能準確得知改變什麼的狀況下對圖層作動畫, 六在不知道UITableView哪一行被添加或者移除的狀況下就能夠平滑的刷新它, 或者在不知道UIViewCVontroller內部視圖層級的狀況下對兩個不一樣的實例作過分動畫. 這些例子的變換不涉及到圖層的屬性, 而是整個圖層樹的改變, 咱們在這種動畫的過程當中手動的在層級關係中添加或者移除圖層.
注意: 要確保CATransition添加到的圖層在過分動畫發生時候不會再樹狀結構中被移除, 不然CATransition會和圖層一塊兒被移除, 通常來講, 只須要將動畫添加到被影響圖層的superlayer
.
例如, 對UITabBarController的視圖圖層作, 切換標籤時候淡入淡出效果.
// 首先設置代理, 而後實現代理方法, 在代理中添加過分動畫. // 自定義切換tabbar時候的過分動畫 - (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController { CATransition *transition = [CATransition animation]; transition.type = kCATransitionFade; transition.duration = 0.2f; // transition.subtype = kCATransitionFromLeft; [self.view.layer addAnimation:transition forKey:nil]; }
過渡動畫是一種對那些不太好作平滑動畫的屬性的強大工具, 可是提供的動畫類型太少了.
另外蘋果經過UIView +transitionFromView:toView:duration:options:completion:和+transitionWithView:duration:options:animations:方法提供了Core Animation的過渡特性。可是這裏的可用的過渡選項和CATransition的type屬性提供的常量徹底不一樣。UIView過渡方法中options參數能夠由以下常量指定:
UIViewAnimationOptionTransitionFlipFromLeft UIViewAnimationOptionTransitionFlipFromRight UIViewAnimationOptionTransitionCurlUp UIViewAnimationOptionTransitionCurlDown UIViewAnimationOptionTransitionCrossDissolve UIViewAnimationOptionTransitionFlipFromTop UIViewAnimationOptionTransitionFlipFromBottom
可是在iOS平臺上仍是能夠作自定義的過渡動畫的. 原理是: 先對目標view進行截圖, 將截圖放在原來view的上面, 而後對截圖進行一些動畫操做, 實現自定義過渡動畫.
截圖: CALayer中有個-renderInContext:
方法,能夠經過把它繪製到Core Graphics的上下文中捕獲當前內容的圖片,而後在另外的視圖中顯示出來。若是咱們把這個截屏視圖置於原始視圖之上,就能夠遮住真實視圖的全部變化,因而從新建立了一個簡單的過渡效果。
這裏有個警告:-renderInContext:捕獲了圖層的圖片和子圖層,可是不能對子圖層正確地處理變換效果,並且對視頻和OpenGL內容也不起做用。可是用CATransition,或者用私有的截屏方式就沒有這個限制了。
以前提到過,你能夠用-addAnimation:forKey:方法中的key參數來在添加動畫以後檢索一個動畫,使用以下方法:
- (CAAnimation *)animationForKey:(NSString *)key;
但並不支持在動畫運行過程當中修改動畫,因此這個方法主要用來檢測動畫的屬性,或者判斷它是否被添加到當前圖層中。
爲了終止一個指定的動畫,你能夠用以下方法把它從圖層移除掉:
- (void)removeAnimationForKey:(NSString *)key;
或者移除全部動畫:
- (void)removeAllAnimations;
動畫一旦被移除,圖層的外觀就馬上更新到當前的模型圖層的值。通常說來,動畫在結束以後被自動移除,除非設置removedOnCompletion爲NO,若是你設置動畫在結束以後不被自動移除,那麼當它不須要的時候你要手動移除它;不然它會一直存在於內存中,直到圖層被銷燬。