2014年5月10日土曜日

【Objective-C】UIActivityIndicatorViewを使用してインジケータを表示する方法

画面表示時に重たい処理をメインスレッドで実施すると、端末がフリーズしたように感じてしまいます。一般ユーザーは画面が表示された瞬間にスクロールやタップを開始するので、数秒間の処理でもフリーズしたと思われてしまいます。
これを回避するにはGCD(rand central dispatch)というマルチスレッドプログラミングを実装します。簡単にいうと、『メインスレッドには重たい処理は書かない、重たい処理は非同期の別スレッドに書く』ということです。今回はバックグラウンドでの重たい処理を実行中にインジケータを表示しています。

では、インジケータのサンプルプログラムを説明します。

まずはヘッダーファイルに、GCDで使用するキューと、インジケータを定義します。

■ヘッダーファイル
dispatch_queue_t _main_queue;
dispatch_queue_t _sub_queue;
UIActivityIndicatorView *_indicator;

Viewのロードイベントでインジケータを設定します。
スタート(開始)するにはstartAnimatingメソッドを使用します、ストップ(停止)するにはstopIndicatorメソッドを使用します。
インジケータに_indicator.hidesWhenStopped = YESを設定しているので、ストップ時に自動的に非表示になるようです。

サンプルプログラムの流れは下記のようになります。
メイン処理
 ・インジケータのスタート
  ▶︎画面にくるくるが表示される

 バックグラウンド処理
  ・通信、集計などの重たい処理
  ・インジケータのストップ(メインスレッドでやる)

ポイントはインジケータのスタート、ストップともにメインスレッドで実装する必要がああるということ。ストップはバックグラウンドの重たい処理が終わったら実施する必要があります。サンプルではperformSelectorOnMainThreadを使用してバックグラウンドからメインスレッドで実施するように書いています。

動作の流れとしてはインジケータがスタートし、バックグラウンドに処理を投げて、画面を表示する。その際に画面上にはくるくるとインジケータが表示されて見える。また、メインスレッドで処理は動いていないため、ユーザーは画面を動かす事もできます。この間バックグラウンドで、画面に表示するためのデータを作成し、作成完了後にバックグラウンド処理からインジケータのストップ処理をメインスレッドで実施するようにしています。

よくあるインジケータが表示されませんという話は、
メイン処理に
・インジケータのスタート
・通信、集計などの重たい処理
・インジケータのストップ
と書いているからです。
このような書き方では、画面にインジケータは表示されません。
上記3つの処理が終わった結果が表示されるだけです。また、メインスレッドでの処理中は画面操作が出来ません。よって、フリーズしているように感じとられてしまいます。


■ソースファイル
- (void)viewDidLoad
{
    [super viewDidLoad];

    // UIActivityIndicatorViewのインスタンス化
    CGRect rect = CGRectMake(0, 0, 100, 100);
    _indicator = [[UIActivityIndicatorView alloc]initWithFrame:rect];
 
    // 位置を指定
    _indicator.center = self.view.center;
 
    // アクティビティインジケータのスタイルをセット
    _indicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
 
    _indicator.hidesWhenStopped = YES;
 
    // UIActivityIndicatorViewのインスタンスをビューに追加
    [self.view addSubview:_indicator];
}

// テーブルデータ作成
- (void) createTableData{
    // メインスレッド用で処理を実行するキューを定義するする
    _main_queue = dispatch_get_main_queue();
    // サブスレッドで実行するキューを定義する
    _sub_queue = dispatch_queue_create("tableLoad", 0);

    // くるくるを表示する
    [_indicator startAnimating];
 
    // サブキュー実行
    dispatch_async(_sub_queue, ^{
        //重たい処理を実装する

        //インジケータ消す
        [self performSelectorOnMainThread:@selector(stopIndicator) withObject:nil waitUntilDone:NO];
        [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
     
        // メインキュー実行
        dispatch_async(_main_queue, ^ {


        });
    });  
}

0 件のコメント:

コメントを投稿