[iOS Animation]-CALayer 顯示動畫 對圖層樹的動畫

對圖層樹的動畫

CATransition並不做用於指定的圖層屬性,這就是說你能夠在即便不能準確得知改變了什麼的狀況下對圖層作動畫,例如,在不知道UITableView哪一行被添加或者刪除的狀況下,直接就能夠平滑地刷新它,或者在不知道UIViewController內部的視圖層級的狀況下對兩個不一樣的實例作過渡動畫。 git

這些例子和咱們以前所討論的狀況徹底不一樣,由於它們不只涉及到圖層的屬性,並且是整個圖層樹的改變--咱們在這種動畫的過程當中手動在層級關係中添加或者移除圖層。 github

這裏用到了一個小詭計,要確保CATransition添加到的圖層在過渡動畫發生時不會在樹狀結構中被移除,不然CATransition將會和圖層一塊兒被移除。通常來講,你只須要將動畫添加到被影響圖層的superlayer。 app

在清單8.2中,咱們展現瞭如何在UITabBarController切換標籤的時候添加淡入淡出的動畫。這裏咱們創建了默認的標籤應用程序模板,而後用UITabBarControllerDelegate的 -tabBarController:didSelectViewController: 方法來應用過渡動畫。咱們把動畫添加到UITabBarController的視圖圖層上,因而在標籤被替換的時候動畫不會被移除。 框架

清單8.12 對UITabBarController作動畫 dom

複製代碼
#import "AppDelegate.h" #import "FirstViewController.h" #import "SecondViewController.h" #import <QuartzCore/QuartzCore.h> @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; UIViewController *viewController1 = [[FirstViewController alloc] init]; UIViewController *viewController2 = [[SecondViewController alloc] init]; self.tabBarController = [[UITabBarController alloc] init]; self.tabBarController.viewControllers = @[viewController1, viewController2]; self.tabBarController.delegate = self; self.window.rootViewController = self.tabBarController; [self.window makeKeyAndVisible]; return YES; } - (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController { //set up crossfade transition CATransition *transition = [CATransition animation]; transition.type = kCATransitionFade; //apply transition to tab bar controller's view  [self.tabBarController.view.layer addAnimation:transition forKey:nil]; } @end
複製代碼

 

自定義動畫

咱們證明了過渡是一種對那些不太好作平滑動畫屬性的強大工具,可是CATransition的提供的動畫類型太少了。 工具

更奇怪的是蘋果經過UIView  +transitionFromView:toView:duration:options:completion: 和 +transitionWithView:duration:options:animations: 方法提供了Core Animation的過渡特性。可是這裏的可用的過渡選項和CATransition的type屬性提供的常量徹底不一樣。UIView過渡方法中options參數能夠由以下常量指定: 學習

複製代碼
UIViewAnimationOptionTransitionFlipFromLeft UIViewAnimationOptionTransitionFlipFromRight UIViewAnimationOptionTransitionCurlUp UIViewAnimationOptionTransitionCurlDown UIViewAnimationOptionTransitionCrossDissolve UIViewAnimationOptionTransitionFlipFromTop UIViewAnimationOptionTransitionFlipFromBottom
複製代碼

 

除了UIViewAnimationOptionTransitionCrossDissolve以外,剩下的值和CATransition類型徹底不要緊。你能夠用以前例子修改過的版原本測試一下(見清單8.13)。 測試

清單8.13 使用UIKit提供的方法來作過渡動畫 動畫

複製代碼
@interface ViewController () @property (nonatomic, weak) IBOutlet UIImageView *imageView; @property (nonatomic, copy) NSArray *images; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //set up images self.images = @[[UIImage imageNamed:@"Anchor.png"], [UIImage imageNamed:@"Cone.png"], [UIImage imageNamed:@"Igloo.png"], [UIImage imageNamed:@"Spaceship.png"]]; - (IBAction)switchImage { [UIView transitionWithView:self.imageView duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{ //cycle to next image UIImage *currentImage = self.imageView.image; NSUInteger index = [self.images indexOfObject:currentImage]; index = (index + 1) % [self.images count]; self.imageView.image = self.images[index]; } completion:NULL]; } @end
複製代碼

 

文檔暗示過在iOS5(帶來了Core Image框架)以後,能夠經過CATransition的filter屬性,用CIFilter來建立其它的過渡效果。然是直到iOS6都作不到這點。試圖對CATransition使用Core Image的濾鏡徹底沒效果(可是在Mac OS中是可行的,也許文檔是想表達這個意思)。 this

所以,根據要實現的效果,你只用關心是用CATransition仍是用UIView的過渡方法就能夠了。但願下個版本的iOS系統能夠經過CATransition很好的支持Core Image的過渡濾鏡效果(或許甚至會有新的方法)。

但這並不意味着在iOS上就不能實現自定義的過渡效果了。這只是意味着你須要作一些額外的工做。就像以前提到的那樣,過渡動畫作基礎的原則就是對原始的圖層外觀截圖,而後添加一段動畫,平滑過渡到圖層改變以後那個截圖的效果。若是咱們知道如何對圖層截圖,咱們就可使用屬性動畫來代替CATransition或者是UIKit的過渡方法來實現動畫。

事實證實,對圖層作截圖仍是很簡單的。CALayer有一個 -renderInContext: 方法,能夠經過把它繪製到Core Graphics的上下文中捕獲當前內容的圖片,而後在另外的視圖中顯示出來。若是咱們把這個截屏視圖置於原始視圖之上,就能夠遮住真實視圖的全部變化,因而從新建立了一個簡單的過渡效果。

清單8.14演示了一個基本的實現。咱們對當前視圖狀態截圖,而後在咱們改變原始視圖的背景色的時候對截圖快速轉動而且淡出,圖8.5展現了咱們自定義的過渡效果。

爲了讓事情更簡單,咱們用UIView -animateWithDuration:completion: 方法來實現。雖然用CABasicAnimation能夠達到一樣的效果,可是那樣的話咱們就須要對圖層的變換和不透明屬性建立單獨的動畫,而後當動畫結束的是哦戶在CAAnimationDelegate中把coverView從屏幕中移除。

清單8.14 用renderInContext:建立自定義過渡效果

複製代碼
@implementation ViewController - (IBAction)performTransition { //preserve the current view snapshot UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES, 0.0); [self.view.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *coverImage = UIGraphicsGetImageFromCurrentImageContext(); //insert snapshot view in front of this one UIView *coverView = [[UIImageView alloc] initWithImage:coverImage]; coverView.frame = self.view.bounds; [self.view addSubview:coverView]; //update the view (we'll simply randomize the layer background color) CGFloat red = arc4random() / (CGFloat)INT_MAX; CGFloat green = arc4random() / (CGFloat)INT_MAX; CGFloat blue = arc4random() / (CGFloat)INT_MAX; self.view.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0]; //perform animation (anything you like) [UIView animateWithDuration:1.0 animations:^{ //scale, rotate and fade the view CGAffineTransform transform = CGAffineTransformMakeScale(0.01, 0.01); transform = CGAffineTransformRotate(transform, M_PI_2); coverView.transform = transform; coverView.alpha = 0.0; } completion:^(BOOL finished) { //remove the cover view now we're finished with it  [coverView removeFromSuperview]; }]; } @end
複製代碼

 

圖8.5

圖8.5 使用renderInContext:建立自定義過渡效果

這裏有個警告: -renderInContext: 捕獲了圖層的圖片和子圖層,可是不能對子圖層正確地處理變換效果,並且對視頻和OpenGL內容也不起做用。可是用CATransition,或者用私有的截屏方式就沒有這個限制了。

在動畫過程當中取消動畫

以前提到過,你能夠用 -addAnimation:forKey: 方法中的key參數來在添加動畫以後檢索一個動畫,使用以下方法:

- (CAAnimation *)animationForKey:(NSString *)key;

 

但並不支持在動畫運行過程當中修改動畫,因此這個方法主要用來檢測動畫的屬性,或者判斷它是否被添加到當前圖層中。

爲了終止一個指定的動畫,你能夠用以下方法把它從圖層移除掉:

- (void)removeAnimationForKey:(NSString *)key;

 

或者移除全部動畫:

- (void)removeAllAnimations;

 

動畫一旦被移除,圖層的外觀就馬上更新到當前的模型圖層的值。通常說來,動畫在結束以後被自動移除,除非設置removedOnCompletion爲NO,若是你設置動畫在結束以後不被自動移除,那麼當它不須要的時候你要手動移除它;不然它會一直存在於內存中,直到圖層被銷燬。

咱們來擴展以前旋轉飛船的示例,這裏添加一個按鈕來中止或者啓動動畫。這一次咱們用一個非nil的值做爲動畫的鍵,以便以後能夠移除它。 -animationDidStop:finished: 方法中的flag參數代表了動畫是天然結束仍是被打斷,咱們能夠在控制檯打印出來。若是你用中止按鈕來終止動畫,它會打印NO,若是容許它完成,它會打印YES。

清單8.15是更新後的示例代碼,圖8.6顯示告終果。

清單8.15 開始和中止一個動畫

複製代碼
@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *containerView; @property (nonatomic, strong) CALayer *shipLayer; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //add the ship self.shipLayer = [CALayer layer]; self.shipLayer.frame = CGRectMake(0, 0, 128, 128); self.shipLayer.position = CGPointMake(150, 150); self.shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage; [self.containerView.layer addSublayer:self.shipLayer]; } - (IBAction)start { //animate the ship rotation CABasicAnimation *animation = [CABasicAnimation animation]; animation.keyPath = @"transform.rotation"; animation.duration = 2.0; animation.byValue = @(M_PI * 2); animation.delegate = self; [self.shipLayer addAnimation:animation forKey:@"rotateAnimation"]; } - (IBAction)stop { [self.shipLayer removeAnimationForKey:@"rotateAnimation"]; } - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { //log that the animation stopped NSLog(@"The animation stopped (finished: %@)", flag? @"YES": @"NO"); } @end
複製代碼

 

圖8.6

圖8.6 經過開始和中止按鈕控制的旋轉動畫

總結

這一章中,咱們涉及了屬性動畫(你能夠對單獨的圖層屬性動畫有更加具體的控制),動畫組(把多個屬性動畫組合成一個獨立單元)以及過分(影響整個圖層,能夠用來對圖層的任何內容作任何類型的動畫,包括子圖層的添加和移除)。

在第九章中,咱們繼續學習CAMediaTiming協議,來看一看Core Animation是怎樣處理逝去的時間。

相關文章
相關標籤/搜索