読者です 読者をやめる 読者になる 読者になる

UIVewのanimateWithDuration〜メソッドを他のUI更新と一緒に実行してはいけない!

昨晩から、約4時間をこの問題の解決に費やしてしまいまいました 。。。。他の人が同じ間違いしないように記録しておきました。

あるアプリに下のような動きをする、簡単なアニメーションを付けました(この画像、コードは説明用サンプルです)。

f:id:yuum3:20141128153650g:plain

失敗のはじまり

コードは以下のように書きました。

@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座標は、画面の中央にあるはずなのに、なぜかアニメーション開始時右端からはじまります (>_<)

f:id:yuum3:20141128153636g:plain

実際のアプリは、こんなにシンプルではないので、いろいろと調べてみましたが・・・・・。 また 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