UINavigationController試しました
一つのiPhoneアプリにいろいろな画面を実装する必要がある場合、よく使われるUINavigationを試しました。
なぜかUIINavigationControllerのサンプルがTableViewControllerとセットになっていたのでわかりやすいようにサンプルを作ってみました。
結果、なぜUIINavigationControllerのサンプルがTableViewControllerなのか、なんとなくわかりました。
なので、UIINavigationControllerのサンプルがTableViewControllerばかりなのは何故かを知っている人はこの記事を読まなくて良いです。
コードについて
Logを出力するためにいたるところにLOG_METHODを仕込んでます。
以下を参考にしています。
http://d.hatena.ne.jp/Psychs/20081120/1227203259
それから私はInterface Builderを使いませんのでxibファイルは削除しています。
下記サイトを参考にしました。
http://webos-goodies.jp/archives/how_to_create_an_iphone_app_without_interface_builder.html
処理を順番に見てみる
最初にmainがよばれて、AppDelegateを呼びます。
#import <UIKit/UIKit.h> int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int retVal = UIApplicationMain(argc, argv, nil, @"AppDelegate"); [pool release]; return retVal; }
次に、AppDelegate.hとAppDelegate.mを見てみます。
// AppDelegate.h #import <UIKit/UIKit.h> @interface AppDelegate : NSObject <UIApplicationDelegate> { UIWindow *_window; UINavigationController *_nav; } @end
#import "AppDelegate.h" #import "RootViewController.h" @implementation AppDelegate #pragma mark - #pragma mark Application lifecycle - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { LOG_METHOD; RootViewController * rootController = [[RootViewController alloc] init]; _nav = [[UINavigationController alloc] initWithRootViewController:rootController]; // IB使わない場合は必要な処理 CGRect frameForWindow = [[UIScreen mainScreen] bounds]; _window = [[UIWindow alloc] initWithFrame:frameForWindow]; [_window makeKeyAndVisible]; [_window addSubview:_nav.view]; return YES; } - (void)dealloc { LOG_METHOD; [_window release]; [_nav release]; [super dealloc]; } @end
以下の処理は、nibファイルを削除してあるので、nibファイルがやっていた処理を自前で記述しています。
// IB使わない場合は必要な処理 CGRect frameForWindow = [[UIScreen mainScreen] bounds]; _window = [[UIWindow alloc] initWithFrame:frameForWindow]; [_window makeKeyAndVisible]; [_window addSubview:_nav.view];
次の処理で起点となるViewControllerのインスタンスを生成しています。
UINavigationControllerのインスタンスは必ず起点となるViewControllerのインスタンスが必要です。
RootViewController * rootController = [[RootViewController alloc] init]; _nav = [[UINavigationController alloc] initWithRootViewController:rootController];
この辺りから推測なので誤りがあるかもしれないのですが、おそらく、UINavigationControllerの内部にViewControllerを配列に保持。
その配列の最後尾を画面表示しています。
その際RootViewController内のViewDidLoadが実行されて画面表示が行われています。
PushMeを押してみる
この時点ではRootViewControllerのインスタンスしか生成されていません。
PushButtonを押すとどうなるでしょうか。ボタンを押すと呼び出されるメソッドは次のようになっています。
// RootViewController.m - (void)buttonDidPush { LOG_METHOD; NextViewController * nextViewController = [[[NextViewController alloc] init] autorelease]; [self.navigationController pushViewController:nextViewController animated:YES]; }
ボタンを押した時点で初めて必要となるインスタンスを生成しています。
pushViewControllerはスタックに新しいViewControllerのインスタンスを格納すると、画面を更新します。
さらにもう一度Push
Hello, next worldの下にあるボタンを押してみます。
UINavigationControllerのスタック最後尾にさらにViewControllerが追加されました。
このように配列状で遷移してきたViewControllerのインスタンスを保持しているので履歴をさかのぼる事ができます。
前の画面に戻るとインスタンスが破棄される
NavigationBarの左側にあるNext Worldを押して前の画面に戻ります。
これはUINavigationControllerの内部でpopViewControllerAnimated:を実行しています。
ログを見ると、以下のように表示されます。
2011-04-05 14:38:20.273 SampleNav[4127:207] -[LastViewController dealloc]
スタックからpopされた際に、参照先が0になります。あとはautoreleaseで自動的にインスタンスが破棄されます。
さらに前の画面に戻ってrootに戻ります。ログをみると、やはりインスタンスが破棄されています。
2011-04-05 14:38:22.656 SampleNav[4127:207] -[NextViewController dealloc]