UIVewのanimateWithDuration〜メソッドを他のUI更新と一緒に実行してはいけない!
昨晩から、約4時間をこの問題の解決に費やしてしまいまいました 。。。。他の人が同じ間違いしないように記録しておきました。
あるアプリに下のような動きをする、簡単なアニメーションを付けました(この画像、コードは説明用サンプルです)。
失敗のはじまり
コードは以下のように書きました。
@interface ViewController () @property (weak, nonatomic) IBOutlet UILabel *scrollLabel; @property (weak, nonatomic) IBOutlet UILabel *statusLabel; - (IBAction)touchStartButton:(id)sender; @end @implementation ViewController - (IBAction)touchStartButton:(id)sender { _statusLabel.text = @"scrolling..."; [UIView animateWithDuration:2.0 animations:^{ _scrollLabel.center = CGPointMake(_scrollLabel.frame.size.width * -0.5, _scrollLabel.center.y); }]; }
このコードは、 statusLabel に scrolling... と表示してから、scrollLabel(黄色いバックグラウンドのラベル)を 2秒かけて、左端に追い出します。
しかし、動かすと下のように動作します ^^; scrollLabelのY座標は、画面の中央にあるはずなのに、なぜかアニメーション開始時右端からはじまります (>_<)
実際のアプリは、こんなにシンプルではないので、いろいろと調べてみましたが・・・・・。 また AutoLayoutだとアニメーションが思ったように動作しないなど記事を見つけ、また、あーだーこーだー・・・。 ちなみに上のサンプルは AutoLayoutは使っていません。
解決編
そこで、シンプルなサンプルを作り実験してみました。すると正しく動作するコードが判りました!! なんと、 statusLabel に @"scrolling..." を代入するのを止めれば良いのです。
あああ! やっと判りました。それをタイトル「UIVewのanimateWithDuration〜メソッドを他のUI更新と一緒に実行してはいけない!」にしました。 iOSでのほとんどの画面操作API等は、呼び出した(statusLabelへの代入も同じく)瞬間に実行されるのではなく、イベント待ちに戻った時に実行されます。上のコードではアニメーションとstatusLabelの表示変更が同事(?)に行われアニメーションが正しくない動作になってしまうのです。
そこで、以下のコードのようにstatusLabelへ代入を行った後でアニメーションを始めれば正しい動作になるのです。(^○^)
#import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UILabel *scrollLabel; @property (weak, nonatomic) IBOutlet UILabel *statusLabel; - (IBAction)touchStartButton:(id)sender; @end @implementation ViewController - (IBAction)touchStartButton:(id)sender { _statusLabel.text = @"scrolling..."; [self performSelector:@selector(scrolling) withObject:nil afterDelay:0.1]; } - (void)scrolling { [UIView animateWithDuration:2.0 animations:^{ _scrollLabel.center = CGPointMake(_scrollLabel.frame.size.width * -0.5, _scrollLabel.center.y); }]; } @end