ViewController#viewDidLoad内でアニメーションを開始してはいけない
このミス定期的にやっちゃうよね!
いや、それはただの物忘れ・・・私の脳が衰えてきているだけか・・・
ビューのアニメーションを開始するのは、ViewControllerのviewDidAppear以降でなければならない。
以下は、ラベルをゆっくりと点滅させるだけの単純な画面である。
【これはダメなやり方ダヨ!】
@interface ViewController : UIViewController @end @implementation ViewController -(void)viewDidLoad { [super viewDidLoad]; // ラベルをビューの中心に貼り付ける UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 30.0)]; label.text = @"Blink"; label.textAlignment = NSTextAlignmentCenter; label.center = self.view.center; [self.view addSubview:label]; // ラベルをフェードイン、フェードアウトさせるアニメーションを開始する CABasicAnimation* blink = [CABasicAnimation animationWithKeyPath:@"opacity"]; blink.duration = 1.95; blink.autoreverses = YES; blink.fromValue = @1.0; blink.toValue = @0.0; blink.repeatCount = HUGE_VALF; blink.fillMode = kCAFillModeBoth; blink.delegate = self; [label.layer addAnimation:blink forKey:@"MyAnimation"]; } @end
上記コードを実際に試した人はきっとこう言うだろう。
「嘘ついてんじゃねえよこのピザ野郎、ちゃんと動くだろ」
実は、上記コードはある状況では動くが、ある状況では動かない。
具体的には、ViewControllerがアニメーションを伴う画面遷移後に表示される場合に、ラベルの点滅アニメーションが機能しない。エラーの類いは何も出ない。
アニメーションが機能しないという部分をもっと細かく述べるならば、アニメーションが開始した直後にすぐに終了してしまう。結果、見た目上は、まったく動かなかったように見える。
答えを知っていればひどくあっけない問題だが、これはviewDidLoad内でアニメーションを開始していることが原因である。
以下のようにすればきちんと動く。
【こちら推奨】
@interface ViewController : UIViewController { CALayer* _blinkLayer; CAAnimation* _blinkAnimation; } @end @implementation ViewController -(void)viewDidLoad { [super viewDidLoad]; UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 30.0)]; label.text = @"Blink"; label.textAlignment = NSTextAlignmentCenter; label.center = self.view.center; [self.view addSubview:label]; // ラベルのアニメーションを生成する(まだ開始しない) CABasicAnimation* blink = [CABasicAnimation animationWithKeyPath:@"opacity"]; blink.duration = 1.95; blink.autoreverses = YES; blink.fromValue = @1.0; blink.toValue = @0.0; blink.repeatCount = HUGE_VALF; blink.fillMode = kCAFillModeBoth; blink.delegate = self; _blinkLayer = label.layer; _blinkAnimation = blink; } -(void)viewDidAppear:(BOOL)animated { // 今です! [_blinkLayer addAnimation:_blinkAnimation forKey:@"MyAnimation"]; } @end
もう少しいえば、本件は、間接的に、「生成と同時に自動的にアニメーションするようなカスタムビューを作ってはならない」という教訓を示している。ViewControllerがviewDidLoad内でサブビューを初期化、構成するのは当然の行為であり、カスタムビューがアニメーションするのであれば、ViewControllerがそのことを知っていなければならないのだ。即ち、カスタムビューがアニメーションするのであれば、初期化時に自動的にアニメーションを開始するのではなく、startAnimatingのようなメソッドを提供しなければならないということになる。
viewDidLoadの時点では、まだビューが表示されておらず、アニメーションを開始するのはビューが表示され「た」直後、即ちviewDidAppearでやるべき、なんて話は、考えてもみれば至極当然のことである。
はぁ・・・