ポップオーバーするテーブルビューに都市名を表示する
ここからはポップオーバーの利用方法の話になりますが、今回はポップオーバーするテーブルビューに世界主要国の首都の名前をメニューとして表示し、最終的にはそこから選んだ都市の地図をベースのビューに表示することにします。
まずは、テーブルビューの中身を表示するために、新たなクラスMenuViewControllerを定義しましょう。もちろんUITableViewControllerのサブクラスです。首都名は、とりあえず5つをcapitalsという文字列の配列として定義しました。これらをテーブルビューの各行に表示するよう、最小限のメソッドも実装します。
元のDetaillViewControllerの方は、ほとんどそのままでいいのですが、2カ所だけ変更しました。1つはポップオーバーを表示するためのボタンの名前で、それを「首都」としています。
もう1つは、そのボタンがタップされたとき、ポップオーバーとして表示するビューコントローラーの種類です。上の例ではデフォルトのテーブルビューコントローラーでしtくぁあ、それを上で定義したMenuViewControllerのクラスに変更します。そうしないと、いつまでも空のテーブルビューしか表示されません。
このプログラムを動かして、ナビゲーションバー上の「首都」ボタンをタップすると、テーブルビューによるメニューが開き、配列として定義した首都の名前が表示されます。
それ以上は、まだプログラムしていないので、ここでどれかの首都を選んでもその名前がハイライトされる以外は何の反応もありません。ポップオーバーは、もう一度「首都」ボタンをタップすれば閉じることができます。
ポップオーバーからベースのビューコントローラーに情報を伝達する
次に、テーブルビューによって作成し、ポップオーバーとして表示したメニューから選んだ項目の情報を、ベースのビューコントローラーに伝達する方法を考えてみましょう。
ポップオーバーとして表示されるテーブルビューは、自分がどうやって誰によって表示されたのかを知りません。そのどこの誰だか知らないビューコントローラーに情報を伝達するために、前回と同様にプロトコルによるデリゲートを定義して使うことにしました。仕組みは前回とまったく同じなので、詳しい説明は省きます。まずは、MenuSelectionDelegateというプロトコルを定義し、MenuViewControllerに、その型のdelegateという変数を用意しておきます。
テーブルビュー上の都市名が選択されたら、デリゲートのmenuSelectedメソッドをその都市名を指定して呼び出します。また、dismissメソッドによってテーブルビューの自身の表示、つまりポップオーバーを閉じています。
一方のDetailViewControllerは、上で定義したプロトコルに準拠させます。また、都市名を表示するためのラベルを用意して画面の中央に配置しています。
さらに、ナビゲーションバー上のボタンがタップされた際には、その場で作成したMenuViewControllerのデリゲートに自分自身を代入して誰がテーブルビューを表示したのかを明らかにしています。デリゲートとして呼び出されるmenuSelectedメソッドも実装して、渡された都市名の文字列をラベルのテキストとして表示します。
このプログラムを実行して、「首都」ボタンをタップすると、都市名のメニューが表示されます。
ここまでは、前と同じですが、こんどは選択直後にメニューのテーブルビューはポップオーバーごと消え、ベースのビューのほぼ中央に、選択した都市名が表示されます。
これで、ポップオーバーをメニューとして利用し、その選択結果がポップオーバーを出現させたほうのビューコントローラーに伝達できることが確かめられました。
メニューから選んだ都市の地図を表示する
選んだ都市名を文字列として表示したのは情報伝達を確かめるためだけです。ここからは、その都市の地図を表示するようにプログラムを追加していきましょう。それもMapKitを使えば簡単です。都市名からジオコーディングによって場所を割り出すのも以前に示したようにそれほど難しくありません。しかし、今回はそこは重要ではないので、簡略化のため、あらかじめ位置を記憶しておくことにしました。そのために、Capitalという構造体を定義して都市名と位置(CLLocationCoordinate2D)をまとめて1つのオブジェクトに記録します。
これまで単なる文字列の配列だったcapitalsは、Capitalの配列に改め、都市名と、その緯度、経度から作った座標オブジェクトをセットにして作成しておきます。
その配列の型の変更にともない、ほかの部分でもいくつか変更が必要になります。まず、デリゲートのmenuSelectedメソッドの引数の型をStringからCapitalに変更します。また、テーブルビューを作成する際のcellForRowAtメソッドの中では、セルのテキストラベルとして、配列の要素そのままではなく、要素のnameプロパティの値をセットするように変更します。
実際に地図を表示するDetailViewControllerの変更は、意外に少なく感じられるでしょう。まず、当然ながらラベルの代わりにマップビューのオブジェクトを作成し、それをこのコントローラーのビュー全体として設定します。
あとは、デリゲートメソッドのmenuSelectedの中で、実際に地図に表示する領域を設定するだけです。
これについても以前にMapKitを取り上げた時に説明していますので詳しくは述べません。引数として渡されてきたCapitalオブジェクトのlocationプロパティと、5km四方の大きさを指定してリージョンを作成し、それをマップビューにセットしています。
このプログラムを起動すると、ベースとなるビューには何も設定していない初期状態の地図が表示されます。
この表示は、ユーザーの環境によって異なる可能性があります。ここから「首都」ボタンをタップして都市を選びます。ここでは「ロンドン」を選んでみました。
その結果、プレイグラウンド画面にはロンドン近辺の地図が表示されます。
次回の予定
今回は、ポップオーバープレゼンテーションコントローラーを利用した「メニュー選択→内容を詳しく表示」というパターンを使って、都市名を選んで地図を表示するアプリに仕立てました。それはほんの一例であって、このパターンは、アイディア次第でどのようなものにも応用できるでしょう。次回からは、複数のビューコントローラーの扱いというテーマから離れて、また少し地味なiOSならではのデータの扱いを取り上げる予定です。