1. 程式人生 > >ios SDK開發之關於CoreAnimation的一些注意點總結

ios SDK開發之關於CoreAnimation的一些注意點總結

   關於UIView animation,很多讀者相信非常喜歡用animation block的動畫API,如下:

  1. Animating Views with Blocks  
  2. + animateWithDuration:delay:options:animations:completion:  
  3. + animateWithDuration:animations:completion:  
  4. + animateWithDuration:animations:  
  5. + transitionWithView:duration:options:animations:completion:  
  6. + transitionFromView:toView:duration:options:completion:  

       當然筆者也一樣,但是關於block還是有一些需要注意的地方的,比如:關於動畫的意外停止,在動畫的時候,突然按Home鍵退出應用。筆者前幾日碰到一個關於類似的UIViewAnimation方面的bug,在這裡記錄一下。請看如下的程式碼:

  1. - (void)animationMoveViewRightOut:(UIView *)view  completion:(void (^)(BOOL finished))completion {  
  2.     [UIView animateWithDuration:1.0f animations:^{  
  3.         CGRect bounds
     = [UIScreen mainScreen].bounds;  
  4.         view.center = CGPointMake( bounds.size.width + view.frame.size.width/2, view.center.y);  
  5.     }completion:^(BOOL finished ) {  
  6.         if (completion) {  
  7.             completion(finished);  
  8.         }  
  9.     }];  
  10. }  
  11. - (void)animationMoveViewToCenter:(UIView *)view {  
  12.     [UIView animateWithDuration:1.0f delay:0.5 options:UIViewAnimationOptionCurveEaseInOut animations:^{  
  13.         CGRect bounds = [UIScreen mainScreen].bounds;  
  14.         view.center = CGPointMake(bounds.size.width / 2, bounds.size.height / 2 );  
  15.     }completion:^ (BOOL finished){  
  16.         if (finished) {  
  17.             [self animationMoveViewRightOut:view completion:^(BOOL finished) {  
  18.                 if (finished) {  
  19.                     NSLog(@"animationMoveViewRightOut!!!");  
  20.                 }  
  21.             }];  
  22.         }  
  23.     }];   
  24. }  

         這段動畫程式碼就是先將某個view動畫移動到螢幕中央,移動到中央的動畫結束的時候接著將該檢視移動出右邊的螢幕之外。你能看出這段動畫有啥問題麼?這段程式碼平時執行的挺好的,完全沒啥問題,但是當執行移動檢視到螢幕中間期間,按下Home鍵,應用進入後臺,再進入前臺的時候,你可能會發現,檢視沒有移動出螢幕之外,動畫被凍結了。為什麼呢?筆者發現,當螢幕進入後臺的時候,UIviewblock動畫會瞬間完成,並執行completion塊動畫,這時傳遞過去的finishedbool值是falsecompletion裡關於移動檢視到螢幕外邊的動畫就不會執行了。

發現這個問題之後,筆者將所有動畫結束的 iffinished)判斷語句去掉。這時在移動檢視到螢幕中央期間再按下Home鍵,讓應用進入後臺,再進入前臺,這時檢視已經看不見了,從列印的資訊能看到 “animationMoveViewRightOut!!!語句,這表明檢視移動到右邊螢幕外的動畫已經瞬間執行了。好,關於UIViewblock動畫注意點總結:1動畫期間,當按下Home鍵時,動畫將會瞬間完成;2.動畫雖然是瞬間完成了,但是completion的返回的finishedbool值是false

      為解決這個問題,如果你不需要在從後臺進入前臺繼續執行動畫,你可以將所有的finish判斷語句去掉,這個讓動畫瞬間完成,防止導致一些意外情況;另外你也可以在進入後臺的時候用[aView.layerremoveAllAnimations];語句來將該檢視的所有動畫remove掉,動畫也會瞬間完成。在此多解釋幾句:[aView.layer removeAllAnimations];僅可以removeCABasicAnimationCoreAnimation動畫,也可以remove掉所有的UIView animation就是類似文章開頭的API動畫以及UIImageViewanimationImagesstartAnimating的序列幀動畫,由此可見,UIImageView的序列幀動畫以及UIView animation的插值動畫的底層實現都是用CoreAnimation實現的。見如下程式碼例項:

  1. aView = [[UIImageView alloc] initWithFrame:CGRectMake(0, viewController.view.frame.size.height /2 - 50, 100, 100)];  
  2.     aView.backgroundColor = [UIColor redColor];  
  3.     aView.animationDuration = 10;  
  4.     aView.animationImages = [NSArray arrayWithObjects: [UIImage imageNamed: @"CloseNormal.png"],[UIImage imageNamed: @"Icon.png"],[UIImage imageNamed:  @"CloseSelected.png"],[UIImage imageNamed:@"Icon-Small-50.png" ], nil];  
  5.     [aView startAnimating];  
  6.     [viewController.view addSubview:aView];  
  7.     [aView release];  
  8.     [self animationMoveViewToCenter:aView];  
  9.     [self performSelector:@selector(removeAnimation:) withObject:nil afterDelay:2];  
  10. - (void)removeAnimation:(id)sender {  
  11. //    [aView stopAnimating];  
  12.     [aView.layer removeAllAnimations];  
  13. }  

          所以,如果你想要能控制更多的動畫內容,比如你想能夠在進入後臺的時候讓動畫停止,進入前臺的時候又讓動畫恢復執行,你可以嘗試用CoreAnimation實現動畫。程式碼例項如下:

  1. - (void)rotationAnimation:(UIView *)view {  
  2.     CABasicAnimation *rotation;  
  3.     rotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];  
  4.     rotation.toValue = [NSNumber numberWithFloat: M_PI * 2];  
  5.     rotation.duration = 5;  
  6.     rotation.cumulative = YES;  
  7.     rotation.repeatCount = 4;  
  8.     rotation.removedOnCompletion = NO;  
  9.     rotation.fillMode = kCAFillModeForwards;  
  10.     rotation.delegate = self;  
  11.     [view.layer addAnimation:rotation forKey:@"rotationAnimation"];  
  12. }  
  13. - (void)animationDidStart:(CAAnimation *)theAnimation {  
  14.     NSLog(@"animationDidStar");  
  15. }  
  16. - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {  
  17.     NSLog(@"animationDidStop finished %d",flag);  
  18. }  
  19. [self rotationAnimation:aView];[self performSelector:@selector(removeAnimation:) withObject:nil afterDelay:2];  
  20. //注意,第一次pauseLayer的時候,就會觸發 - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag 函式,以後將不會再次觸發,或者[aView.layer removeAllAnimations];也能觸發;總之,- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag只會被觸發一次,觸發一次之後,再次發生能觸發該函式的情況將不再呼叫該函式。  
  21. -(void)pauseLayer:(CALayer*)layer //暫停動畫  
  22. {  
  23.     CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];  
  24.     layer.speed = 0.0;  
  25.     layer.timeOffset = pausedTime;   
  26. }  
  27. -(void)resumeLayer:(CALayer*)layer //恢復動畫  
  28. {  
  29.     CFTimeInterval pausedTime = [layer timeOffset];  
  30.     layer.speed = 1.0;  
  31.     layer.timeOffset = 0.0;  
  32.     layer.beginTime = 0.0;  
  33.     CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;  
  34.     layer.beginTime = timeSincePause;  
  35. }  
  36. - (void)applicationDidBecomeActive:(UIApplication *)application {  
  37.     /*  
  38.      Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.  
  39.      */  
  40.     [self resumeLayer:aView.layer];  
  41. }  
  42. - (void)applicationDidEnterBackground:(UIApplication *)application {  
  43.     /*  
  44.      Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.  
  45.      If your application supports background execution, called instead of applicationWillTerminate: when the user quits.  
  46.      */  
  47.     [self pauseLayer:aView.layer];  
  48. }  

         好了,再次總結一下,呼叫以上-(void)pauseLayer:(CALayer*)layer函式,可以暫停一個layer tree的所有animation;而呼叫上面提供的-(void)resumeLayer:(CALayer*)layer函式可以恢復一個layer tree的所有animation。本篇到此結束。