woshidan's blog

あいとゆうきとITと、とっておきの話。

mixi iOS Training 第三回分メモ

ようやく最後まで復習した。

それにしても、PeatexからIT系と同じカテゴリのイベントとして「酵素の力でからだの中からキレイに酵素玄米 class」がおすすめされてきたので私は……。

参考にしているのは、公式資料の https://github.com/mixi-inc/iOSTraining/wiki/2.1-UINavigationController で、

ソースコードは、https://github.com/mixi-inc/iOSTraining/wiki/2.1-UINavigationControllerから引用させていただいて、一部日本語コメントが入ってるのは自分が足したものです。

内容

  • UINavigationController
    • 用途
    • 使い方
  • 演習1
    • 階層の数字をNavigationBarに表示
    • Viewの位置調整がうまくできない
  • NavigationBar UINavigationItem
    • コンテンツビューコントローラに右側にpopボタンを実装してみる
  • カスタマイズとUIAppearnce protocol
    • AppDelegate.mとは
  • 演習2
    • Segueを実行する場合の方法
    • 次のUIVIewControllerのインスタンスをpushする場合の方法

UINavigationController

用途

UIViewController(一枚一枚の画面)を階層的に管理する上のほうのメニューバーがあるような実装において使用します。 下図(View Controller Programming Guide for iOSより引用)の赤い部分がUINavigationControllerの部分。

f:id:woshidan:20150214173057p:plain

緑の部分はUINavigationControllerが持っているスタックの中で管理されて、スタックの一番上にあるものが表示される感じです。

Custom Content View Controllerみたいな語句を見て、一部、緑の部分をコンテンツビューコントローラと書いているのだけど、俺俺用語の気がします。

使い方

  1. アプリケーションが起動したときに表示されるUIViewControllerの- viewDidApperar内で、 rootとなるUIViewControllerとUINavigationControllerのインスタンスをそれぞれ作成。 UIViewControllerをrootにセットしたUINavaigationControllerを表示させる。

  2. UINavigationControllerの管理下でUIViewControllerの表示・使用を管理するには、 UIViewControllerの中で、次のUIViewControllerをpushする導線を書いておく必要がある。 (popについては戻るボタンがデフォルトで実装されているのでいまは気にしない)

  3. UIViewControllerをスタックにpushするときはNavigationControllerで管理されているUIViewControllerのほうから、 自身を管理しているnavigationControllerを呼び出し、- pushViewController:viewController animated:YESのメッセージを送る。 (このviewControllerは次にpushするUIViewControllerのクラスのインスタンスであり、メッセージを送る前にインスタンスを作成しておく)

演習1

そのUIViewControllerはいくつめのスタックなのか

スタックが何枚目か、ってどこで取得したらいいかよく分からなかったので結局サンプルコード見ました。

## UINavigationControllerで管理される側のUIViewController(コンテンツビューコントローラ?)の実装ファイル(.m)
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.title = [NSString stringWithFormat:@"%ld", self.navigationController.viewControllers.count];
    // UIViewControllerのtitleはNavigationBarの真ん中や戻るボタンなどの見出しに使われたりするプロパティ。
}

self.navigationController.viewControllers.countらしい。

Viewの位置調整がうまくできない

このイベント足したら、今度は、ボタンが表示されなくなってしばらく??ってなりました。

下の方に各コントローラ名のラベルを追加したら、コンテンツビューコントローラ側のラベルは出ているので、 (エラーメッセージの出し方、見方がよく分かっていない……)

ボタンが上すぎたみたいです。

titleなどが追加されたため、文字表示の文NavigationBarが大きくなったせいのような気はするけど、 このへんの位置の管理は回が進めばありそうな気がしたから放置。

NavigationBar UINavigationItem

UINavigationItemはUINavigationBarに乗っている「戻る」「タイトル」「編集モードへ切り替え」などの部品。

こういった部品はコンテンツビューコントローラごとに変わるものだから、 デフォルトからカスタマイズしたい場合は、コンテンツビューコントローラのクラスで実装していくみたいです。

例:さっきの例のNavigationBar右上方にコンテンツビューコントローラのスタックをpopさせるボタンを設置してみる

- (void)viewDidLoad
{
    [super viewDidLoad];
    UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithTitle:@"pop"
                                                                    style:UIBarButtonItemStylePlain
                                                                   target:self
                                                                   action:@selector(pressPopButton)];
    // 作成したButtonをnavigationItemのrightBarButtonItemプロパティに代入
    self.navigationItem.rightBarButtonItem = rightButton;
}

// UIBarButtonItemを作成するときにactionにセットした関数
- (void)pressPopButton
{
    [self.navigationController popViewControllerAnimated:YES];
}

カスタマイズとUIAppearnce protocol

UIAppearance を用いると、特定の UIComponent のデザインを一括して変更することが出来ます。

// MixiAppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // 略
    [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"customNavBarImage1"] forBarMetrics:UIBarMetricsDefault];
    [[UIBarButtonItem appearance] setTintColor:[UIColor blackColor]];
    // 略
}

ところで。

AppDelegate.mとは

を読んでいるけど、よく分からないので、また読むと思います。

この記事は、Cocoa Touchフレームワークの概要みたいなものの気がするから、 これ理解できて多少クラスとメソッドが思い浮かんだら、 自分が今iOSやっている目的は達成するからゴール地点みたいなものとして定期的に読み直すといいのかも。

AppDelegateはアプリが起動したり終了したり、アプリ自身のライフサイクルを司るメソッドを扱う印象をうけたのですが、今回だと起動したときにNavigationBarの設定をするぞ、ということなのでしょうか。

演習2

遷移図どおりのほうは当日の演習のときにやったとので、 segueを使って渡したデータの切り替えで頑張ってみました。

結果のコードはこちら

https://github.com/mixi-inc/iOSTraining/tree/master/SampleProjects/2.1/NavigationPractice

とほとんど同じなので、概要だけ。

両方法とも2つめのUIViewControllerに表示させたい文字列を管理するNSString変数をプロパティとして宣言しておきます。

Segueを実行する場合の方法

  1. 各ボタンをタップした時のイベントハンドラ内にUIViewController自身のプロパティとして、次のViewControllerで表示する文字列を持たせたあと、segueを実行。
  2. segueを実行したときに呼び出される-(void)prepareForSegueメソッドにおいて、segueが自身のプロパティとして持っている次のViewControllerを呼び出して、 そのViewControllerのプロパティに1の文字列を代入
  3. この場合、segueの表示方法はShowみたいです。
  4. 2つめのUIViewControllerのPreseinting Seguesにて、segueのrelationshipの設定をボタンから矢印引っ張った時のままではなく、1つ目のUIViewController/manualにしておきます。

次のUIVIewControllerのインスタンスをpushする場合の方法

  1. 各ボタンのイベントハンドラ内で次に表示させるUIViewControllerのインスタンスを作成し、そのインスタンスのプロパティとしてボタンに対応する文字列を次に表示させるUIViewControllerのプロパティに持たせます
  2. 自身を管理しているUINavigationControllerを呼び出して1で作成したUIViewControllerのインスタンスをpushします。

今回のようにデータ構造は同じな場合では、prepareForSegueでデータを代入するほうが簡単そうでしたが、 次の画面で必要な情報の構造が一緒というわけでなければこちらのほうが融通が利きそうな気がしました。

実際ではどうするのかはまあよくわからないのですが。