本アプリケーションは「Navigation-Based Application」テンプレートを使用しています。今回は、このテンプレート内容を解説した後、UINavigationControllerが管理しているUITableViewControllerのサブクラス(RootViewController)の実装へと進みます。
「Navigation-Based Application」テンプレートの場合、UINavigationControllerオブジェクトはMainWindow.xibに含まれており、管理対象としてUITableViewControllerのサブクラスであるRootViewControllerが指定されています。 Interface BuilderでMainWindow.xibをオープンし、ウィンドウに表示されたNavigation Controllerアイコンをダブルクリックすると、デバイスサイズのウィンドウがオープンします。そこに...
Loaded From "RootViewController.nib"
と記載されていますが、これは、この場所に "RootViewController.nib"ファイルのFile's Ownerとして定義されているビューコントローラ(クラス)が読み込まれて表示されることを示しているわけです。これを変更したい場合には、先んじてUINavigationControllerのビュー表示(点線の角丸で囲まれている領域)を選択しておき、Inspectorウィンドウの「Root View Controller Attributes」(ひとつ目アイコン選択)で表示される「NIB Name」のクラス名を入力し直します。アプケーションを起動すると、一番最初に、ここに入力されているビューコントローラの管理ビューが、スクリーンに表示されることになります。
さて、RootViewController本体の方はRootViewController.xibの方に用意されています。このnibファイルは、Xcodeから直接オープンできますが、UINavigationControllerの "RootViewController.nib"表記(青文字部分)をクリックすることで即座にオープンすることも可能です。必要とされるアウトレット(Outlets)の接続はテンプレート自体が準備してくれていますが、自分自身で調整する場合もありますので、重要な箇所をピックアップしておきたいと思います。アウトレットの接続状況は、Inspectorウィンドウの「Root View Controller Connections」(2つ目のアイコン選択)で確認できます。
まず最初に、File's Owner(RootViewControllerクラス)のtableViewとviewアウトレットが、同じnibファイルに登録されているテーブルビューアイコン(Table Viewと表記されている)に接続されていることを確認してください。 このアウトレットの設定が正しく行われていないと、アプリケーション起動時にテーブルビューが表示されません。次にテーブルビューのアイコンをクリックして、同じくアウトレットの接続状態を確認します。こちらは、dataSourceとdelegateアウトレットが「File's Owner」アイコンに接続されていることを確認してください。こちらのアウトレットの設定が正しくないと、後からソースコードでクラス実装を行った時に、関連メソッドが正しく働かないことになります。
次に、ソースコード処理を順番に追いかけてみます。まず、ナビゲーションコントローラを準備する処理は、アプリケーションデリゲートクラス(SymmetryAppDelegeta.m)のapplicationDidFinishLaunching:メソッド(デリゲート)に記述されています。
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
// ここにアプリケーション起動時に必要な処理を記述する
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
ここで使われているwindowとnavigationControllerという2つのアウトレットは、テンプレートにより用意されており、MainWindow.xibの「App Delegate」アイコンに接続されています。処理内容は、ウィンドウ(UIWindow)のサブビューにナビゲーションコントローラのビューを配置し、そのウィンドウをキーウィンドウとします。つまり、ナビゲーションコントローラで指示したテーブルビューコントローラ(RootViewControlle)がnibファイルから読み込まれ、最終的には、それが管理しているテーブルビュー(UITableView)が表示されるわけです。
ここから、ようやRootViewController.mに実装したメソッドの出番となります。まずは、クラス定義のRootViewController.hの方ですが、今のところインスタンス変数はひとつも定義されていません。クラスが対応しているプロトコルについては、以前お話した「画像の取り込み処理」に必要とされるクラスのものが3つ定義されています。
#import <UIKit/UIKit.h>
@interface RootViewController : UITableViewController < UINavigationControllerDelegate,UIActionSheetDelegate,UIImagePickerControllerDelegate >
{
}
@end
次は、クラス実装をするRootViewController.mです。すでに、テンプレートがオーバライドすべきメソッドを幾つかピックアップし、コメント行として用意してくれています。このうちどれをオーバライドすべきなかは、クラスの処理内容によって違ってきます。
- (void)viewDidLoad
{
[super viewDidLoad];
}
viewDidLoadは、ビューコントローラがnibファイルから呼び込まれる時に一度だけ呼ばれます。通常、ビューコントローラの「初期化」処理に用いられます。
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
viewWillAppear:は、ビューコントローラに属するビューがスクリーン上に表示される直前に呼ばれます(この時点でまだビューは表示はされていない)。このメソッドは、一番最初だけでなく、別のビューコントローラから切り替わる時にも呼ばれますので御注意ください。 通常、ビューコントローラの「準備」処理に用いられます。
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
viewDidAppear: は、ビューコントローラに属するビューがスクリーン上に表示された直後に呼ばれます。
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
viewWillDisappear:は、ビューコントローラに属するビューがスクリーンから消える直前に呼ばれます。このメソッドはアプリの終了だけでなく、別のビューコントローラへの切り替え時にも呼ばれます。通常、ビューコントローラの「後始末」処理に用いられます。
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
viewDidDisappear:は、ビューコントローラに属するビューがスクリーンから消された直後に呼ばれます。
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
shouldAutorotateToInterfaceOrientation:は、ビューコントローラの向き(デバイスに対して縦方向なのか?横方向なのか?)を指示します。デフォルトは縦向きだけを容認するソースコードの記述となっています。
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
didReceiveMemoryWarningは、メモリの使用状況がタイト(メモリ不足)になった場合にOSから呼ばれます。開発者は、ここに不用になったメモリ(キャッシュやテンポラリ作業領域など)を開放する処理を記載します。ただし、間違ったメモリ解放(まだ必要なのに...)をするとクラッシュの原因となりますので、くれぐれも注意してください。ここで何もしなくとも、スーパクラスのビューコントローラではメモリ領域を増やすために不用ビューを解放するといった処理が実行されます。
- (void)dealloc
{
[super dealloc];
}
deallocでは、通常のクラスと同じく、このビューコントローラクラスで仕様したインスタンス変数を解放します。
上記メソッドの中には、先んじてスーパクラスを呼び出す必要の無い種類もありますが(スーパクラスは何もしていないと言うこと)今後仕様変更があるかもしれませんので、必ずスーパクラスを呼ぶようにしましょう。ちなみに、didReceiveMemoryWarningやdeallocでは今でもスーパクラスの呼び出しは必須です。
ところで、少し上級者向けの話となりますが、initWithNibName:bundle:でnibファイルからUIViewControllerを読み込んで表示する時にpushViewController: animated:を使うと、最初の処理ではUIViewController側でオーバライドしたメソッドが...
- (void)viewDidLoad
- (void)viewWillAppear:
- (void)viewWillDisappear:
の順番で呼ばれます。しかし呼び出しにpresentModalViewController:animated:を使うと、何故だか...
- (void)viewWillAppear:
- (void)viewDidLoad
- (void)viewWillDisappear:
の順番でメソッド呼ばれます。この現象はv2.2になってから起こるようになり、v2.1では問題ありませんでした。iPhone OS 3.0では修正されている様ですので、この現象はバグだったようです。v2.2をアプリ起動対象とされている方は御注意ください。
次回は、テーブルビュー(UITableView)に必要とされるデータソース(dataSource)とデリゲート(delegate)メソッドの解説を行います。これらの仕組みは、Mac OS XのNSTableViewと似ているのですが、iPhone OS独自の仕組みも沢山見受けられますので、そちらを中心に話を進めたいと思います。