在iOS上實現動畫效果,基本都是在一段給定的時間內完成狀態的連續變化,包括背景色、Frame大小、位移、旋轉、透明度、縮放等。
老的動畫實現形式:
iOS 4.0之前,蘋果提供的是類似於數據庫中的事務編程的模式:
例如實現一個view的淡入效果,可以通過設置view的alpha實現(alpha = 1是全透明):
[UIView beginAnimations:nil context: nil];
[UIView setAnimationDuration:1.0]; //動畫的時長是1s
[UIView setAnimationDelay:0.0]; //不延時執行
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationStopped)];//動畫完成後執行一些操作
self.view.alpha = 0.0;//最終的alpha為0,不透明
self.view.frame = CGRectMake(10, 10, 50, 50);//最終的frame
[UIView commitAnimations];//提交動畫
實現了一個1s中的動畫,將view的透明度設為不透明,frame設為(10,10,50,50)。
用beginAnimations標識開始動畫,然後設置動畫的各種屬性,最後通過commitAnimations提交動畫,之後系統接管該動畫並執行。
Block動畫實現形式:
iOS4.0之後,蘋果提供了一組重載函數,實現了以block的形式執行動畫:
(void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations
(void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
(void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
(void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
比如下面這樣:
+ (void)fadeIn: (UIView *)view andAnimationDuration: (float) duration{
[view setAlpha:0.0];
NSLog(@"begin animation");
[UIView animateWithDuration:duration animations:^{
[view setAlpha:1.0];
NSLog(@"in animation block");
} completion:^(BOOL finished) {
NSLog(@"completion animation");
}];
NSLog(@"exit fadeIn");
}
通過參數duration、delay、options等分別設置動畫的時間、延時、執行選項。
在block animations中實現動畫的主體,在block completion中實現動畫完成後需要執行的邏輯。之所以有completion,是給我們一個在動畫跑完成後執行一些邏輯的入口(老的動畫方式通過setAnimationDidStopSelector實現)。
假如你想在動畫執行完成後做一些事情,那麼把代碼放在NSLog(@"in animation block")和NSLog(@"exit fadeIn")是有本質區別的。因為我們把動畫組裝好後就交給操作系統了,會立刻走到NSLog(@"exit fadeIn"),並不會阻塞主線程流程。
completion block會有一個BOOL類型參數,用來告知動畫是否真的執行完成了。這個參數的意義在於我們可以在completion中判斷之前的動畫是否真的執行完了,因為動畫是有可能被取消的(可以通過[view.layer removeAllAnimations]取消動畫),但無論是否取消,系統都會調用completion告知動畫執行結束(想象扔手雷的場景,在與地面發生碰撞時手雷飛行的動畫結束,接下來要執行爆炸的動畫,但如果手雷飛行時被人接住,那麼就需要取消飛行動畫,至於後面是否爆炸......)。甚至如果我們設置了動畫執行的時間duration是0時,completion也會被調用(雖然duration是0,但仍是異步的,系統會在下一個消息循環的開始時立刻調用completion),此時BOOL參數仍然是YES。
前面說到提交動畫給系統後,動畫並不會阻塞主線程,但有時候我們希望在動畫結束前不要有其他邏輯進來,這可以通過在提交動畫後執行消息循環輪詢變量狀態實現:
+ (void)fadeIn: (UIView *)view andAnimationDuration: (float) duration andWait:(BOOL) wait{
__block BOOL done = wait; //wait = YES wait to finish animation
[view setAlpha:0.0];
[UIView animateWithDuration:duration animations:^{
[view setAlpha:1.0];
} completion:^(BOOL finished) {
done = NO;
}];
// wait for animation to finish
while (done == YES)
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
}
常見的動畫實現:
縮放--改變view.transform
view.transform = CGAffineTransformMakeScale(0, 0);
[UIView animateWithDuration:duration animations:^{
view.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
}];
淡入淡出--改變view.alpha
[view setAlpha:0.0];
[UIView animateWithDuration:duration animations:^{
[view setAlpha:1.0];
} completion:^(BOOL finished) {
}];
位置移動--改變view.center
[UIView animateWithDuration:duration animations:^{
view.center = CGPointMake(view.center.x - length, view.center.y);
} completion:^(BOOL finished) {
}];
旋轉--改變view.transform
[UIView animateWithDuration:duration animations:^{
view.transform = CGAffineTransformMakeRotation(degreesToRadians(angle));
} completion:^(BOOL finished) {
}];
動畫嵌套:
在一個動畫執行完成後繼續執行動畫,甚至執行多個動畫形成動畫鏈:
這個帶回彈的氣泡彈出動畫通過串行的3個改變view縮放值的動畫實現,在completion中繼續一個新的動畫。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KCjxwcmUgY2xhc3M9"brush:java;">self.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.001, 0.001);
__unsafe_unretained NearbyGroupMapAnnotationView* reself = self;
[UIView animateWithDuration:0.3/1.5 animations:^{
reself.transform = CGAffineTransformScale(CGAffineTransformIdentity, 1.1, 1.1);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3/2 animations:^{
reself.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.9, 0.9);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3/2 animations:^{
reself.transform = CGAffineTransformIdentity;
}];
}];
}];
首先將view縮放到正常狀態的0.001倍作為初始態,
動畫1:開始將view放到到1.1倍
動畫2:將view縮小到0.9倍
動畫3:將view回復正常大小
減速動畫
(void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
這個函數可以通過下面兩個很有意思的參數實現類似彈簧的效果(帶加速度):
usingSpringWithDamping:(CGFloat)dampingRatio
initialSpringVelocity:(CGFloat)velocity
上圖中有兩個動畫效果,一個是平穩的減速停止,一個是減速後帶有回彈(類似碰撞)。
usingSpringWithDamping標識了彈簧的強度,介於0~1之間。為1時,動畫將會平滑且沒有上下擺動的減速到他的最終狀態,當該值小於1時,動畫的擺動會越來越厲害。
initialSpringVelocity用來標識view開始收到彈簧作用力時的速度,介於0~1之間,假設整個動畫將移動100,當取1時,表示速度為100/1s。initialSpringVelocity越小速度越小。
其實,不用上面這個復雜的方法,通過串行多個相反方向的位移動畫,(分別控制他們的動畫時間模擬加速度),也可以實現回彈的效果。