You are on page 1of 46

我所知道的

CoreAnimation
Weizhong Yang
a.k.a zonble
zonble@gmail.com
http://zonble.net

Monday, March 28,


今天用到的
Sample Code

https://github.com/zonble/CAShowcase
New BSD License

Monday, March 28,


Why CoreAnimation?

• 這年頭動畫效果非常重要,尤其是 iPad
• 當 UIView 提供的動畫不夠用的時候,又
還不需要用到自己寫 OpenGL,就該用
CoreAnimation 了。

Monday, March 28,


CoreAnimation 是?

• 一套 Mac/iOS framework
• 製作各種平面動畫
• UIView 動畫其實算是 CoreAnimation 的
wrapper

Monday, March 28,


使用 CoreAnimation
的挑戰

• 要習慣 concurrent 的程式寫作


• 不過,反正做軟體最難的部份往往都不
是程式…

Monday, March 28,


使用 CoreAnimation
的挑戰

• 難的往往是設計與溝通
• 在設計階段很難用 Mockup 溝通-怎麼
講都很難讓對方理解最後會產生什麼動
畫,除非把程式寫出來…
• 如果不是對方要的,那程式就白寫了…

Monday, March 28,


使用 CoreAnimation

• 都是 Objective-C 物件
• 可喜可賀

Monday, March 28,


重要的 Class

• CALayer:演員
• CAAnimation:腳本
• CATransaction:整體情境設定

Monday, March 28,


第一步

• #import <QuartzCore/QuartzCore.h>

Monday, March 28,


CALayer

Monday, March 28,


CALayer

• iOS
• 每個 UIView 都有一個 layer([aView
layer] or aView.layer)
• Mac
•NSView 要特別設定 setWantsLayer:
• 今天以 iOS 為主

Monday, March 28,


CALayer 的 Interface 很像
View

• 有 frame、bounds、background color
• 把一個 layer 放到另外一個 layer 上面,
是用 addSublayer
• 加入之後,也可以知道自己的 superlayer

Monday, March 28,


CALayer 也不像 View

• 不是 responder,不處理各種 event
(Click、Touch…)
• 大部分 property 一改就會產生動畫
• 繪製內容的方法也稍微不一樣

Monday, March 28,


設定 CALayer

• CALayer *aLayer = [CALayer layer];


• aLayer.contents = (id)[UIImage
imageNamed:@”test.jpg”].CGImage;
• 設定圖片內容時,其實就已經有 fade in/
out 效果

Monday, March 28,


設定 CALayer

• aLayer.position = CGPointMake()...
• aLayer.bounds = CGRectMake()...
• 都會直接產生動畫效果
• 預設動畫時間為 0.25 秒

Monday, March 28,


產生動畫的方法

• 直接改 Layer 的 property 就會有動畫


• 自己使用 CAAnimation 物件做動畫
• 後面再講…

Monday, March 28,


CATransaction 1
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue
forKey:kCATransactionDisableActions];
// 調整 CALayer
[CATransaction commit];
// 這樣會關閉各種動畫效果

Monday, March 28,


CATransaction 2
[CATransaction begin];
[CATransaction setValue:[NSNumber
numberWithFloat:2.0] forKey:
kCATransactionAnimationDuration];
// 調整 CALayer
[CATransaction commit];
// 這樣會改變動畫時間

Monday, March 28,


先提一下 CATransition…
CATransition *t = [CATransition animation];
t.type = kCATransitionMoveIn
t.subtype = kCATransitionFromRight;
aLayer.contents = (id)[UIImage
imageNamed:@”test.jpg”].CGImage;
[aLayer addAnimation:t forKey:@"Transition"];
// 產生移動效果

Monday, March 28,


CATransition

• 有很多 Private API(flip、cube)


• 用了會不會被 reject? 呃… 不知道。

Monday, March 28,


CAAnimation

Monday, March 28,


CAAnimation

• CABasicAnimation
• CAKeyframeAnimation
• CAAnimationGroup
• CATransition …前面講了

Monday, March 28,


CABasicAnimation
• [CABasicAnimation
animationWithKeyPath:...]
• @”position”
• @”bounds”
• @”opacity”
• @”contents”...
Monday, March 28,
CABasicAnimation

• 還有
• @”transform.rotation.x”
• @”transform.rotation.y”
• @”transform.rotation.z”
• ...

Monday, March 28,


CABasicAnimation

• fromValue = [NSValue...];
• toValue = [NSValue ...];
• duration = 3.0; //Seconds
• repeatCount = NSUIntegerMax; // Forever

Monday, March 28,


CAKeyframeAnimation

• 不是用 fromValue、toValue,而是用
CGPathRef
• CAKeyframeAnimation *a =
[CAKeyframeAnimation
animationWithKeyPath:@"position"];
• a.path = (CGPathRef)path;

Monday, March 28,


CAAnimationGroup

• CAAnimationGroup *group =
[CAAnimationGroup animation];
• group.animations = [NSArray
arrayWithObjects:anim1, anim2, nil];

Monday, March 28,


執行 CAAnimation

• [aLayer addAnimation:myAnimation
forKey:@"MyKey"];

Monday, March 28,


fill mode

• 一個動畫結束,預設 layer 會跳回到動畫


前的狀態
• animation.fillMode =
kCAFillModeForwards;
• 這樣才會在結束的時候不動…

Monday, March 28,


另外要注意一下
Property 的改變
CABasicAnimation *a = [CABasicAnimation
animationWithKeyPath:@”bounds”];
a.fromValue = [NSValue valuwWithCGRect:fromRect];
a.toValue = [NSValue valuwWithCGRect:toRect];
[aLayer addAnimation:a forKey:@"boundsAnimation"];
NSLog(@”bounds:%@”, NSStringFromCGRect(aLayer.bounds));

• 還是會看到原本的 bounds,而不是動畫的 toValue 喔。

• 也就是說,在 layer 執行動畫的時候,去查看 layer 的 property,往往會跟


你目前看到的不一樣。

Monday, March 28,


我的 Layer 要跑好幾段動畫,怎樣

一個動畫做完
再做一個動畫

Monday, March 28,


一個動畫做完
再做一個動畫
• [aLayer addAnimation:myAnimation1
forKey:@"MyKey1"];
• [aLayer addAnimation:myAnimation2
forKey:@"MyKey2"];
• 這兩個動畫會同時發生,不是一個跑完
才跑下一個

Monday, March 28,


話說你要這樣寫
也是可以啦…
• [self performSelector:@selector
(runAnimation1) withObject:nil afterDelay:
1.0]; // 一秒
• [self performSelector:@selector
(runAnimation2) withObject:nil afterDelay:
2.0]; // 二秒
• 很醜,完全不建議
Monday, March 28,
方法一:
設定 beginTime
• animation1.beginTime =
CACurrentMediaTime() + 1.0; // 一秒
• animation2.beginTime =
CACurrentMediaTime() + 2.0; // 二秒
• 但有時候 CAAnimation 會被卡住,然後
就被一起觸發,這樣不好…

Monday, March 28,


什麼時候 animation 會被
卡住?
• layer 所在的 view 不在最上面
• 已經先寫了動畫,才去產生用來放置
layer 的 UIView,再把 layer 放進
view.layer 裡
• app 在背景
• runloop 有東西卡住
• 總之,真的很常發生
Monday, March 28,
方法二:
用 delegate
animation.delegate = self;
[myLayer addAnimation: animation forKey:@”key”];
...
- (void)animationDidStop:(CAAnimation *)
theAnimation finished:(BOOL)flag {
// Fire another animation
}
// 這樣才會確實在第一個動畫結束時跑另一個動

Monday, March 28,


delegate 如何辨識是哪個
animation?…有點鳥蛋
animation.delegate = self;
animation.removedOnCompletion = NO;
[myLayer addAnimation: animation forKey:@”key”];

...

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {


if (theAnimation == [myLayer animationForKey:@”key”]) {
// Fire another animation
[myLayer removeAllAnimations];
}
}

// 目前還沒想到更好的辦法

Monday, March 28,


λ 抱怨 λ

• 一個 CAAnimation 結束的時候應該要有
block 可以跑啊啊啊啊啊啊啊…
• iOS 5 會有嗎?
• Lion 好像還沒看到

Monday, March 28,


一些撇步

Monday, March 28,


自動排列 CALayer?

• UILayer -layoutSublayers
• 我也常用 UIView -layoutSubviews 就是
了…

Monday, March 28,


不設定 contents,而是在
CALayer 中用 Quartz 畫圖
- (void)drawInContext:(CGContextRef)ctx
{

UIGraphicsPushContext(ctx);
// 把 ctx 變成 current graphics context 來用 :D

UIBezierPath *path = [UIBezierPath
bezierPathWithRoundedRect:self.bounds cornerRadius:10.0];


CGContextSaveGState(ctx);

CGContextAddPath(ctx, path.CGPath);

CGContextClip(ctx);

[image drawInRect:self.bounds];

CGContextRestoreGState(ctx);

UIGraphicsPopContext();
}

Monday, March 28,


透過某個 view 的 layer 取得這
個 view 畫面

UIGraphicsBeginImageContext(self.bounds.size);
CGContextRef ctx = UIGraphicsGetCurrentContext();
[self.layer renderInContext:ctx];
UIImage *anImage =
UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

return anImage;

// 這個很好用 :D

Monday, March 28,


最後

Monday, March 28,


在 Mac 上面,不要在用到
Cocoa Drawing 的 NSView 中加
上 CALayer,不要混用…
因為會慢到爆

Monday, March 28,


別忘了 Accessibility
• 讓視障朋友也可以順利使用 app
• 不過大部分人好像都不太注意…
• 一般的 UIView/UIControl 都支援
Accessibility,但是如果拿 CALayer 做成
按鈕,需要自己實作 Accessibility 支援
• 不過今天也沒時間細說…

Monday, March 28,


完了
Das ist alles.

Monday, March 28,

You might also like