さて、まだ5章が続きます。
「Cocoa touch プログラミングの中心」とうたわれているだけあって、
ページ数も多いんです。 *01まあ、内容の重要度とページ数は必ずしも比例しませんが。
と言うわけで、今回読む章は…
5 ビューコントローラーのパターン
の続き。
5.3 ビューコントローラの配置の仕方
ビューコントローラの生成と利用について、実例を交えて解説されている。
5.3.1. ナビゲーションコントローラに入れる
UINavigationControllerクラスを利用して、ビューコントローラの管理を行う。
pushViewController:animated:と言うメソッドを使って、
ナビゲーションコントローラが管理する階層の最後にビューコントローラを追加する。
実例
// コントローラをインスタンス化する
RSSItemListController* controller;
controller = [[RSSItemListController alloc] init];
// 自動解放する
[controller autorelease];
// ナビゲーションコントローラに追加
[self.navigationController pushViewController:controller animated:YES];
ポイントは、自動解放してから追加している点。
こうすることで、
ビューコントローラの管理をナビゲーションコントローラに任せる。
結果、自身は参照を保持する必要がなくなる。
注意点は、ビューコントローラからの通知の受け取り。
これについては「7.2.5. コントローラ間の通知のパターン」を参照。 *02本の記述では、「8.2.5.」 となっているが、これは誤記。
5.3.2. モーダルビューとして表示する
UIViewControllerのpressModalViewController:animated:メソッドを使う。
これもナビゲーションコントローラに入れてしまい、自身では参照を保持しない。
実例
- (IBAction)feedAction {
// コントローラを作成
RSSFeedListController* controller;
controller = [[RSSFeedListContoller alloc] init];
// 自動解放する
[controller autorelease];
// ナビゲーションコントローラ作成
UINavigationController* navController;
navController = [[UINavigationController alloc]
initWithRootViewController:controller];
// こっちも自動解放
[navController autorelease];
// モーダルビューとして表示する
[self presentModalViewController:navController animated:YES];
}
問題は、モーダルビューの隠蔽。
通常はdismissModalViewControllerAnimated:メソッドを、
モーダルビューとして表示されているコントローラに対して呼び出す。
が、この本では、モーダルビューの隠蔽もビューを作成したコントローラに持たせる。
上記の実例はRSSChannelListControllerがRSSFeedListControllerを、
モーダルビューとして表示している。
この場合、ビューの隠蔽もRSSChannelListControllerに行わせたい。
そのためには、コントローラ間で通知を行う仕組みがいる。
これも詳細は「7.2.5. コントローラ間の通知のパターン」を参照。 *03ここも、本の記述では、「8.2.5.」 と誤記。
どうも7章が8章になってしまっているようだ。
5.3.3. 手動で配置する
UIViewのaddSubview:を使用する。
ただし、ビューコントローラとして守らなければならない手順がある。
実例
// コントローラを作成
_controller = [[RSSFeedListController alloc] init];
// 大きさを設定
CGRect rect;
rect = self.view.bounds;
_controller.view.frame = rect;
// ビューを追加
[self.view addSubview:_controller.view];
// UIViewControllerのメソッドを呼び出し
[_controller viewWillAppear:NO];
[_controller viewDidAppear:NO];
ポイントは、まず大きさを調整すること。
その後viewWillAppear:とviewDidAppear:を呼び出す。
これにより、ビューコントローラとして必要な処理が動く。
注意点としては、ライフサイクル管理も自前で行なってやる必要があること。
適切なタイミングでコントローラを破棄する様にする。
5.4 ナビゲーションアイテム
ナビゲーションバーの両端位置に配されるボタン。
具体的には、
UINavigationItemクラスの、
leftBarButtonItem と rightBarButtonItem のプロパティ。
操作しやすい場所にあるため、
ユーザーインタフェースとしては、かなり重要。
5.4.1. UIBarButtonItem の作成と更新
作成方法は2つ。
Interface Builder で作成する方法と、ソースコード中で作成する方法。
Interface Builder は、
ライブラリからドラッグ&ドロップでボタンを配置できるなど、
グラフィカルに作成できる。
ソースコード中で作成する場合は、
viewDidLoadのメソッドで作成する。
UIBarButtonItemには、initWithImage:style:target:action や、
initWithTitle:style:target:actionなどのメソッドで初期化を行う。
実例
- (void)_updateNavigationItem:(BOOL)animated {
// ナビゲーションアイテムの設定
[self.navagationItem setLeftBarButtonItem:_feedItem animated:animated];
[self.navigationItem setRightBarButtonItem:_refreshItem animated:animated};
}
最初の呼び出しはviewWillAppearになる。
- (void)viewWillAppear:(BOOL)animated {
...
// 画面を更新
[self _updateNavigationItem:animated];
...
}
上記の様にviewWillAppear内で呼び出されることで、
画面更新時に確実にナビゲーションアイテムも更新される。
_updateNabigationItemのanimatedフラグは、
viewWillAppear:のanimatedフラグを利用するため。
5.4.2. 編集ボタンとedittingプロパティ
編集ボタンあUIViewControllerのeditButtonItemメソッドで取得できる。
これはUIViewControllerのeditingプロパティと連動しており、
ボタンのタイトルが「編集(Edit)」と「完了(Done)」で切り替わる。
当然、編集画面への切り替えが必要。
setEditing:animatedメソッドを上書きして実装する。
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
// 親クラスのメソッドを呼び出す
[super setEditing:editing animated:animated];
// 画面を更新する
[self _updateNavigationItemAnimated:animated];
}
_updateNavigationItemAnimated:では、
editingプロパティの値に応じてナビゲーションアイテムを更新する。
実例
- (void)_updateNavigationItemAnimated:(BOOL)animated {
// 編集モードの場合
if ( self.editing ) {
// ナビゲーションアイテムの設定
[self.navigationItem setLeftBarButtonItem:nil animated:animated];
[self.navigationItem setRightBarButtonItem:nil animated:animated];
}
// 通常モードの場合
else {
// ナビゲーションアイテムの設定
[self.navigationItem setLeftBarButtonItem:_addItem animated:animated];
[self.navigationItem setRightBarButtonItem:_addItem animated:animated];
}
}
5.4.3. キャンセルと保存
編集と並んで、定番のナビゲーションアイテム。
ポイントは、いきなりモデルオブジェクトを操作しないこと。
保存ボタンsaveActionと、キャンセルボタンcancelActionを用意し、
データ編集時は、ビューのインスタンス値のみを編集。
saveActionが呼ばれたときに、始めてモデルオブジェクトを編集する。
実例
- (IBAction)saveAction {
// 編集中のチャンネルを取得
RSSChannel* channel;
channel = _channel;
// チャンネルがない場合は新たに作成
if ( !channel ) {
// 新規チャンネルを作成
channel = [[RSSChannel alloc] init];
[channel autorelease]:
// チャンネルを追加
[[RSSChannelManager shareManager] addChannel:channel];
}
// タイトルを設定
channel.title = _titleTextField.text;
// URLを設定
channel.feedUrlString = _urlTextFields.text;
// チャンネルの配列を保存する
[[RSSChannelManager shareManager] save];
...
}
5.5 ツールバー
基本的にナビゲーションアイテムと同様。
状況に応じたボタンの使用と、余計なメモリの使用回避。
そのため、更新は独立したメソッドで行う。
実例
- (void)_updateToolbarItems:(BOOL)animated {
// ツールバーを表示
[self.navigationController setToolbarHidden:NO animated:animated];
// ツールバーアイテムの設定
NSArray* toolbarItems;
toolbarItems = [NSArray arrayWithObjects:_merkItem, nil];
[self setToolbarItems:toolbarItems animated:animated];
}
注意点は、ツールバーアイテムの表示⁄非表示 の切り替えも、
このメソッド内で行っている点。
ツールバー表示のビューから非表示のビューに切り替わるときも、
このメソッドを呼び出してやることで、ツールバーを非表示にしてやることができる。
【おまけ】はじめてみたは良いけれど
「iOS開発におけるパターンによるオートマティズム」 *04毎度思うが、長いタイトルだわー。 を読み、ブログに書こう!
と思い立って、考えなしに実行してから、はや20日。
結局、ぐだぐだと本の内容をなめるだけの記事が続いて、かなり後悔。
本の内容への理解が不足しているので、たぶん的外れな事もいっぱい書いている。
そもそも、他人様に読んでもらう作りになっていない。 *05本の内容を垂れ流しているだけの記事になっている気もしている。
いったん止めて、もっと練った書き方をした方が良いとも考えるが、
まずは、このまま1周するつもり。
その上で、見せ方含めて考えた2周目が書ければ良いなー。
などと夢想しつつ、早くひと通りを終えたい今日この頃です。

