Cocoaはやっぱり!
インターネットにアクセスしよう
Web Kit #4 : WebViewと仲間たち

今回のテーマ

前回、「 コーディングせずにWebブラウザを作ってしまおう 」というやや無謀な企画を行いましたが、やはり、コーディングなしではとりあえず動く程度のブラウザしかできません。そこで、早速コーディングと行きたいところですが、今回は準備体操として、WebViewやWebView周辺のクラスについて概要を見ていきたいと思います。

推奨環境 この解説は、以下の環境を前提に作成し、動作確認等を行っています。ご確認ください。

改版履歴

WebViewとWebFrameViewの見た目

ウィンドウの中にWebViewを配置すれば、そこにWebページが表示されました。しかし、WebViewが直接Webページを表示しているわけではありません。WebViewは、1つのWebFrameViewという子供のビューを内包しています。実際にWebページを表示するのはWebFrameViewの方です。画面上の見た目としては、WebViewとWebFrameViewは同じ大きさでぴったり重なっています ( 図は見やすくするためにちょっとずらして書いてあります )。

さて、わざわざ2つのビューを使用するには訳があります。HTMLには、フレームという機能がありますよね。HTMLの中に別のHTMLを表示することができるという「 入れ子の構造 」を持っています。ということで、それを実現するクラスも入れ子の構造にする方が管理しやすいのです。上の図は、フレームを使っていないWebページでの図でして、フレームを使ったWebページの場合は以下のようになります。

これは、左右に2分割されている場合の例ですが、WebView自身が1つのWebFrameViewを持っていて、さらに、その中に2つのWebFrameViewが存在しているわけです。分かりやすく言うと、HTMLファイル1つにWebFrameViewが1つ対応するということです。フレームタグを使っていないページでも、一番外側の枠をフレームとみなすことで、統一した考え方でいけるというわけです。ということで、WebViewは全てのフレームを表示する場所を確保しているような、そういった存在ということになります。

ちなみに、どちらもNSViewのサブクラスです。

縁の下のWebFrame

先程、WebViewとWebFrameViewの「 見た目上 」の関係を書きましたが、内部的な関係も説明します。

一番の親はやはりWebViewです。先程はWebViewがWebFrameViewを所有している親のように書きましたが、詳細を見ていくとWebViewはWebFrameというクラスのインスタンスを所有していて、このWebFrameがWebFrameViewを所有しています。このWebFrameというのが各フレームのデータ管理をするクラスで、それを各フレームのコンテンツを表示するのがWebFrameViewといった役割分担になっています。WebFrameとWebFrameViewはこのように通常はペアで存在します。

フレーム分割されているページの場合は、WebFrameがWebFrameの配列を持つことになります。図の右側は子供のフレームですが、左のトップのフレームがchildFramesという配列を持っていて、その下にWebFrameとWebFrameViewのペアが存在していることを表しています。この図は、先程も出てきた左右2つに分割している場合の例です。分割数が多かったり、孫フレームがある場合もあります。

WebViewには必ず1つのWebFrameが存在しまして、これはmainFrameメソッドで取得できます。各WebFrameの所有するWebFrameViewを取得したい場合はframeViewメソッド、WebFrameの子供のフレームを管理しているWebFrameの配列を取得するにはchildFramesメソッドを使います。

前回、コーディングなしでWebブラウザが動きましたが、トップのHTMLを読み込むように指示をすると、データをダウンロードした後、画面に表示したり、フレーム分割されていれば、WebFrameとWebFrameViewを生成されます。さらに、別のページにジャンプした際にフレームの分割が解除される場合は、適切に、WebFrameやWebFrameViewが破棄されます。このような処理が全て自動的に行われていたことが分かります。細かな動きを変えたい場合は、デリゲート側をいろいろなタイミングで呼び出してきますので、そちらで指示を出すことになります。

動きを変えるデリゲート

NSURLDownloadやNSURLConnectionと同様に、WebViewにもデリゲートの仕組みがあります。細かな制御を行うためにデリゲートも通知の内容によって複数種類あります。このデリゲートの部分をコーディングすることで様々な動作の細かな指示を出すことができるようになります。

WebFrameLoadDelegate - Webページを構成する各フレームの読み込み状態の通知を受けるためのデリゲートです。各フレームの読み込み開始/終了/エラー/タイトル決定等の通知が来ます。

WebResouceLoadDelegate - Webページを構成する各リソースの読み込み状態の通知を受けるためのデリゲートです。リソースと言っているのは、HTML、画像、スクリプト、スタイルシートなどを指します。各リソースの読み込み開始/終了/エラー等の通知が来ます。

DownloadDelegate - コンテンツをファイルへダウンロードする際のデリゲート。NSURLDownloadのデリゲートです。各リソースの読み込み開始/終了/エラー等の通知が来ます。

WebUIDelegate - ユーザインターフェイス周りの通知を受けるためのデリゲートです。マウスをWebView上で動かしたときに、その下にあるオブジェクト ( 画像、リンク等 ) について通知を受けたり、WebViewの表示サイズの変更の通知を受けたりします。

WebPolicyDelegate - 新しいアクセスを発生させる前に、どんな動作を行うべきか、表示するかダウンロードするか動作を禁止するか等を判断するデリゲート。

アプリケーションの規模が大きくなってくると、通知の内容によって異なるインスタンスに通知を出してほしい場合がでてくることを考慮してこのようになっています。規模が小さい場合は、全てのデリゲートを1つのインスタンスに割り当てても構いません。