CoreAnimation手記(二)顯式動畫

屬性動畫

屬性動畫 最用于圖層的某個單一屬性,并指定了它的一個目標值,或者一連串要做的動畫值.
屬性動畫分 基礎動畫和關鍵幀動畫

基礎動畫

最簡單的形式就是從一個值改變到另一個值,這也是CABasicAnimation最主要的功能。

CABasicAnimation是CAPropertyAnimation的一個子類,而CAPropertyAnimation的父類是CAAnimation

一.第一個類CABasicAnimation

筆記: CABasicAnimation 的三個重要屬性 id fromValue id toValue id byValue,為了防止沖突,不能一次性同時給這三個值賦值.

有一個注意的是,設置動畫,過度到一個新的顏色,但是動畫結束之后,又立刻變回原來的顏色.

<code><pre>
Type Object Type Code Example

CGFloat NSNumber id obj = @(float);

CGPoint NSValue id obj = [NSValue valueWithCGPoint:point);

CGSize NSValue id obj = [NSValue valueWithCGSize:size);

CGRect NSValue id obj = [NSValue valueWithCGRect:rect);

CATransform3D NSValue id obj = [NSValue valueWithCATransform3D:transform);

CGImageRef id id obj = (__bridge id)imageRef;

CGColorRef id id obj = (__bridge id)colorRef;
</code></pre>
第一個解決方案:
在動畫開始之前或者動畫結束之后。

<code><pre>
CALayer *layer = self.colorLayer.presentationLayer ?:self.colorLayer;

animation.fromValue = (__bridge id)layer.backgroundColor;

[CATransaction begin];

[CATransaction setDisableActions:YES];

self.colorLayer.backgroundColor = color.CGColor;

[CATransaction commit];
</code></pre>

說明這里了設置圖層的顏色屬性,禁止了隱式動畫,因為這里的圖層是獨立的,不是UIVIew中得

<code><pre>
//這里封裝了一個方法替代 setValue animation to value直接applyBasicAnimation 這樣,動畫完了以后,設置

的屬性變化也會對應修改 這個代碼可以復制過去
- (void)applyBasicAnimation:(CABasicAnimation *)animation toLayer:(CALayer *)layer{
//set the from value (using presentation layer if available)
animation.fromValue = [layer.presentationLayer ?: layer valueForKeyPath:animation.keyPath];

//update the property in advance //note: this approach will only work if toValue != nil

[CATransaction begin];

[CATransaction setDisableActions:YES];

[layer setValue:animation.toValue forKeyPath:animation.keyPath];

[CATransaction commit];

//apply animation to layer

[layer addAnimation:animation forKey:nil];
}

  - (IBAction)changeColor{ 

//create a new random color

CGFloat red = arc4random() / (CGFloat)INT_MAX; CGFloat green = arc4random() / (CGFloat)INT_MAX;

CGFloat blue = arc4random() / (CGFloat)INT_MAX;

UIColor *color = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];

//create a basic animation

CABasicAnimation *animation = [CABasicAnimation animation];

animation.keyPath = @"backgroundColor";

animation.toValue = (__bridge id)color.CGColor;

//apply animation without snap-back

[self applyBasicAnimation:animation toLayer:self.colorLayer];
}

</code></pre>

說明這里 使用keyPath 修改layer的一個屬性動畫,實際上它是一個關鍵路徑(一些用點表示法可以在層級關系中指向任意嵌套的對象),而不僅僅是一個屬性的名稱,因為這意味著動畫不僅可以作用于圖層本身的屬性,而且還包含了它的子成員的屬性,甚至是一些虛擬的屬性(后面會詳細解釋)。

第二個解決方案:

實現CAAnimationDelegate 協議中的delegate

- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag
{
//set the backgroundColor property to match animation toValue
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.colorLayer.backgroundColor = (__bridge CGColorRef)anim.toValue;
[CATransaction commit];
}

注意:使用委托模式而不是一個完成塊會帶來一個問題,就是當你有多個動畫的時候,無法在在回調方法中區分哪個圖層調用的。

引用:

不幸的是,即使做了這些,還是有個問題,清單8.4在模擬器上運行的很好,但當真正跑在iOS設備上時,我們發現在-animationDidStop:finished:委托方法調用之前,指針會迅速返回到原始值,這個清單8.3圖層顏色發生的情況一樣。

問題在于回調方法在動畫完成之前已經被調用了,但不能保證這發生在屬性動畫返回初始狀態之前。這同時也很好地說明了為什么要在真實的設備上測試動畫代碼,而不僅僅是模擬器。

我們可以用一個fillMode屬性來解決這個問題,下一章會詳細說明,這里知道在動畫之前設置它比在動畫結束之后更新屬性更加方便。

第二個類:CAKeyframeAnimation

<code><pre>
@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *containerView;

@end

@implementation ViewController

- (void)viewDidLoad

{
[super viewDidLoad];
//create a path

UIBezierPath *bezierPath = [[UIBezierPath alloc] init];

[bezierPath moveToPoint:CGPointMake(0, 150)];

[bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(75, 0) controlPoint2:CGPointMake(225, 300)];

//draw the path using a CAShapeLayer

CAShapeLayer *pathLayer = [CAShapeLayer layer];
pathLayer.path = bezierPath.CGPath;
pathLayer.fillColor = [UIColor clearColor].CGColor;
pathLayer.strokeColor = [UIColor redColor].CGColor;
pathLayer.lineWidth = 3.0f;
[self.containerView.layer addSublayer:pathLayer];

//add the ship
CALayer *shipLayer = [CALayer layer];
shipLayer.frame = CGRectMake(0, 0, 64, 64);
shipLayer.position = CGPointMake(0, 150);
shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage;
[self.containerView.layer addSublayer:shipLayer];
//create the keyframe animation
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position";
animation.duration = 4.0;
animation.path = bezierPath.CGPath;
[shipLayer addAnimation:animation forKey:nil];

}

@end

</code></pre>

運行示例,你會發現飛船的動畫有些不太真實,這是因為當它運動的時候永遠指向右邊,而不是指向曲線切線的方向。你可以調整它的affineTransform來對運動方向做動畫,但很可能和其它的動畫沖突。

幸運的是,蘋果預見到了這點,并且給CAKeyFrameAnimation添加了一個rotationMode的屬性。設置它為常量kCAAnimationRotateAuto(清單8.7),圖層將會根據曲線的切線自動旋轉(圖8.2)。

animation.rotationMode = kCAAnimationRotateAuto;

第三個類 CATransision

CATransision可以對圖層任何變化平滑過渡的事實使得它成為那些不好做動畫的屬性圖層行為的理想候選。蘋果當然意識到了這點,并且當設置了CALayer的content屬性的時候,CATransition的確是默認的行為。但是對于視圖關聯的圖層,或者是其他隱式動畫的行為,這個特性依然是被禁用的,但是對于你自己創建的圖層,這意味著對圖層contents圖片做的改動都會自動附上淡入淡出的動畫。

這段翻譯的讓我真的是很費解.在這里順便說明下:

CATransision 可以對圖層上發生的任何變化,(屬性或者內容大小),都可以添加一個平滑過渡的動畫.CATransition的動畫是默認的隱式動畫.

過渡動畫和之前的屬性動畫或者動畫組添加到圖層上的方式一致,都是通過-addAnimation:forKey:方法。但是和屬性動畫不同的是,對指定的圖層一次只能使用一次CATransition,因此,無論你對動畫的鍵設置什么值,過渡動畫都會對它的鍵設置成“transition”,也就是常量kCATransition。

過渡動畫和屬性動畫的區別,兩個動畫的添加方式一直,不過一個是默認的值,(transition),一個可以自定義.

一段神奇的代碼

<code><pre>

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

</code></pre>

UIVIew提供的過渡動畫函數

[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];

一個自定義過渡動畫的代碼

<code><pre>
@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];
}];

}

</code></pre>

在動畫過程中取消動畫

添加動畫
- (CAAnimation *)animationForKey:(NSString *)key;

刪除動畫
- (void)removeAnimationForKey:(NSString *)key;

或者移除所有動畫:

 - (void)removeAllAnimations;
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容