さて、まだ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周目が書ければ良いなー。
などと夢想しつつ、早くひと通りを終えたい今日この頃です。