● Carbon視点でiPhone探求(2009/07/06)

  このニュースは、MOSAの会員にのみ配布されているデベロッパー向けの
  デジタルマガジンMOSADeNのに掲載された記事です。ほぼ一ヶ月遅れで
  ここに掲載されて行きます

 〜 UITableViewControllerを実装していく 〜


本アプリケーションは「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独自の仕組みも沢山見受けられますので、そちらを中心に話を進めたいと思います。

copyright 2009 Ottimo, Inc. All rights reserved
無断転載・引用禁止