プログレスバーを表示する

プログレスバーを表示する

ここまででとりあえずHTML を読み込めるようになったけど、実際に動かしてみる と、あれっ?と、思うかもしれない。というのもURL を入れても、しばらくは無反応 だからだ。裏で読み込みが始まってから、実際にレンダリング画像を表示するまで、間 がある。そこで、読み込み状態を表示するための、プログレスバーを追加しよう。

6.1 WebResourceLoadDelegate

読み込み状態を知るには、これもまたデリゲートを使うんだ。 WebResourceLoadDelegate というデリゲートを使うと、テキストや画像といっ た、リソースを読み込んでいる最中の状態を知ることができる。

6.1.1 リソース識別子

ところで、リソースとは一体なにか?それは、HTML で示されているテキストや画 像のことなんだ。通常、HTML で使われるリソースはホスト上の1 つのファイルで、 それはそれぞれのURL で表現される。たとえば、メインのHTML ファイルである index.html、タイトル画像であるtitle.jpg、というようにね。

だけど、実際にHTML を読み込みはじめると、リソースは途中でURL が変わる可能性があるんだ。たとえば、URL がリダイレクトされるように設定されていると、リ ソースのURL は変更される。これをWebKit では、読み込みのセッションを始めた ら、URL が変更したとしても同一のリソースとしてみなすんだ。ということは、リ ソースを区別するために、URL によらないユニークな識別子を設定してやる必要があ る。これを、ユーザが設定することになるんだ。WebResourceLoadDelegate のた めのメソッド、webView:identifierForInitialRequest:fromDataSource: がこれを行う。


WebKit/WebResourceLoadDelegate.h

- (id)webView:(WebView*)sender
    identifierForInitialialRequest:(NSURLRequest*)request
    fromDataSource:(WebDataSource*)dataSource;

このメソッドは、引数としてWebView と、リソースの初期のURL が渡される。こ のURL は、リソースを読み込んでいる最中に、変更される可能性があるので注意。 で、このメソッドの返り値として、リソースの識別子を返すんだ。たとえば、このメ ソッドが呼び出された順に、シーケンシャルな整数値を返す、っていう実装が考えられる。


(sample)

- (id)webView:(WebView*)sender
      identifierForInitialRequest:(NSURLRequest*)request
      fromDataSource:(WebDataSource*)dataSource
{
  // リソース識別子として、resourceCount の NSNumber を作成します
  NSNumber*  number;
  number = [NSNumber numberWithInt:resourceCount++];
  return number;
}


新たなリソースの読み込みが始まる毎にこのメソッドが呼び出されるので、これでユ ニークな識別子を作ることができるんだ。これは、このメソッドが呼び出された回数を 数えておけば、読み込まれるリソースの数を知ることができる、っていうことも意味してるよ。

6.1.2 読み込みの成功と失敗

リソースの読み込みが成功したら、 webVeiew:resource:didFinishLoadingFromDataSource: が呼び出されるんだ。

WebKit/WebResourceLoadDelegate.h

- (void)webView:(WebView*)sender
  resource:(id)identifier
  didFinishLoadingFromDataSource:(WebDataSource*)dataSource;

引数として、ユーザが設定したリソース識別子が渡されるので、どのリソースの読み 込みが終わったか、知ることができる。

失敗した場合は、webView:resource:didFilaLoadingWithError:fromDataSource: だ。

WebKit/WebResourceLoadDelegate.h

- (void)webView:(WebView*)sender
  resource:(id)identifier
  didFailLoadingWithError:(NSError*)error
  fromDataSource:(WebDataSource*)dataSource;

このメソッドにも、同じくリソース識別子が渡される。失敗の原因は、 WebFoundation で定義されているNSError を見ることで分かるよ。

この2 つのメソッドを使えば、いまどれだけの数のリソースが読み込まれたか、ま たは失敗したか、が分かるんだ。これで、プログレスバーが作れるぜ。

6.2 実装

デじゃ、実際に作ってみよう。まず、nib ファイルを編集して、レイアウトを変更しよ う。これは、前回のプロジェクトの続きとして説明するよ。

■プログレスバーのためのnib ファイルの編集

1. MyDocument.nib を開く。MyDocument.nib をダブルクリックして、InterfaceBuilder を立ち上げてくれ。

2. ウィンドウに、プログレスバーとテキストフィールドを追加する。ウィンドウを開 いて、プログレスバーを置くためのスペースを作ろう。どこでもいいんだけど、 WebView を少し小さくして、下の方に配置することにしてみた。ここに、読み込み状 態を示すプログレスバーと、文字で示すためのテキストフィールドを追加する。デキス トフィールドには、'3 of 5' というように、いくつリソースを読み込んだかを表示する ようにしよう。下の図のように配置してくれ。

layout-1.jpg
図6-1 ウィンドウのレイアウト

ここでは、プログレスバーの最大値は1.0 にした。また、テキストフィールドは初 期設定の文字列を消すと、見づらくなるので注意。

3. MyDocument にアウトレットを追加する。Classes タブに移動して、 MyDocument クラスを選択してくれ。ここに、プログレスバーとテキストフィールド を指し示すためのアウトレットを追加する。それぞれ loadProgressBar、 loadStatusTextField という名前にしよう。

outlets.jpg
図6-2 MyDocument のアウトレット

4. アウトレットを接続する。Instances タブに戻って、File's Owner アイコンを選択 してくれ。そこから、Ctrl キーを押しながらドラッグする。すると、線が引き出され るので、新しく追加されたプログレスバーとテキストフィールドにつなごう。それぞ れ、アウトレット laodProgressBar と loadstatusTextField に「Connect」してくれ。

ここまでできたら、セーブしてProject Builder に戻ろう。

■ソースコードの編集

5. MyDocument.h を編集する。まず、Interface Builder での変更に対応するため に、アウトレットを追加しよう。loadProgressBar とloadStatusTextFiled という インスタンス変数を追加する。

さらに、リソースの数を数えるカウンタのためのインスタンス変数を追加しよう。プ ログレスバーを表示するために必要な情報は、読み込まれるリソースの数と、実際に読 み込まれたリソースの数だ。WebKit では、読み込まれたリソースの数を、成功した読 み込みと失敗した読み込みに分けることができる。だから、それぞれに対応する、3つのインスタンス変数を追加しよう。こんな感じにしてくれ。

MyFirstBrowser/MyDocument.h

@interface MyDocument : NSDocument
{
  IBOutlet id webView;
  IBOutlet id urlTextField;
  IBOutlet id loadStatusTextField;
  IBOutlet id loadProgressBar;

  /* 読み込むリソースの数 */
  int resourceCount;
  /* 読み込みに成功したリソースの数 */
  int resourceCompletedCount;
  /* 読み込みに失敗したリソースの数 */
  int resourceFailedCount;
}

6. MyDocument.m を編集する。ここでやるべきことは、デリゲートの設定、カウン タの初期化、デリゲートメソッドの実装、そしてプログレスバーの表示だ。

◆デリゲートの設定

まずは、デリゲートの設定を。これは、前と同じく、 windowControllerDidLoadNib: メソッドの中でやってやろう。次のように、 setResourceLoadDelegate: の呼び出しを追加するんだ。

MyFirstBrowser/MyDocument.m

- (void)windowControllerDidLoadNib:(NSWindowController*)windowController
{
  // WebView のデリゲートを設定します
  ...
  [webView setResourceLoadDelegate:self];
}


◆カウンタの初期化

続いて、カウンタの初期化。カウンタのためのインスタンス変数は、新しい読み込み のセッションが始まる度にリセットしよう。それには webView:didStartProvisionalLoadForFrame: が使える。

MyFirstBrowser/MyDocument.m

- (void)webView:(WebView*)sender
    didStartProvisionalLoadForFrame:(WebFrame*)frame
{
  // フレームがメインフレームの場合
  if (frame == [sender mainFrame]) {
    ...

    // リソースのカウンタをリセットします
    resourceCount = 0;
    resourceCompletedCount = 0;
    resourceFailedCount = 0;
  }
}


◆デリゲートメソッドの実装

そして、WebResourceLoadDelegate のためのメソッドを追加する。今回追加するのは、上で説明した3つだ。

まず、リソース識別子を返すための
webView:identifierForInitialRequest:fromDataSource:。このメソッドは識 別子を返すんだけど、同時に読み込むべきリソースの数を数えることもできる。そこ で、このメソッドで、リソースのカウンタ resouceCount の値を1 増やそう。

MyFirstBrowser/MyDocument.m

- (id)webView:(WebView*)sender
    identifierForInitialRequest:(NSURLRequest*)request
    fromDataSource:(WebDataSource*)dataSource
{
  // リソース識別子として、resourceCount の NSNumber を作成します
  NSNumber* number;
  number = [NSNumber numberWithInt:resourceCount++];
  return number;
}

次に、読み込み成功の webView:resource:didFinishLoadingFromDataSource:。ここでは、成功カウン タの resourceCompletedCount を増やす。増やしたら、後で説明するプログレス バーを更新するための _updateResourceStatus を呼び出すんだ。

MyFirstBrowser/MyDocument.m

- (void)webView:(WebView*)sender
    resource:(id)identifier
    didFinishLoadingFromDataSource:(WebDataSource*)dataSource
{
  // リソース読み込み完了カウントを増やします
  resourceCompletedCount++;

  // リソース読み込み状態を更新します
  [self _updateResourceStatus];
}

3 つ目は、読み込み失敗のための webView:resource:didFailLoadingWithError:fromDataSource:。同じよう に、失敗カウンタを増やして、プログレスバーを更新する。

MyFirstBrowser/MyDocument.m

- (void)webView:(WebView*)sender
    resource:(id)identifier
    didFailLoadingWithError:(NSError*)error
    fromDataSource:(WebDataSource*)dataSource
{
  // リソース読み込み失敗カウントを増やします
  resourceFailedCount++;

  // リソース読み込み状態を更新します
  [self _updateResourceStatus];
}


◆プログレスバーの表示

最後のメソッドは、プログレスバーとテキストフィールドを更新するための、_updateResourceStatus だ。プログレスバーに設定する値は、リソースカウンタの 値から求めることができる。テキストフィールドに表示する文字も、同様ね。

MyFirstBrowser/MyDocument.m

- (void)_updateResourceStatus
{
  // プログレスバーの設定をします
  if (resourceCount == 0) {
    [loadProgressBar setDoubleValue:0.0];
  }
  else {
    double value;
    value = (resourceCompletedCount + resourceFailedCount) /
        (double)resourceCount;
    [loadProgressBar setDoubleValue:value];
  }

  // リソースカウントの文字列を作成します
  NSString* status;
  status = [NSString stringWithFormat:@"%d of %d",
    resourceCompletedCount + resourceFailedCount, resourceCount];
  [loadStatusTextField setStringValue:status]; 
}

7. ビルドして実行する。プログレスバーが機能することを確認してくれ。


browser-2.jpg
図6-3 MyFirstBrowser3 動作図

これで、読み込みのときに、変なストレスを感じないようなると思うよ。


■ここまでのプロジェクト:
MyFirstBrowser3.zip