Kikuchy's Second Memory

つくる楽しさをもっと伝えたい。プログラムを書いていて、わからなかったこと・気付いた事を書き留めています。

Segue で画面遷移するときにパラメーターを渡してみる

昨日はTableViewを使ったUIを作っていたのですが、おなじみの


テーブルのセルをタップしたら、関連情報が次の画面で表示される


という動作をやってみたかったので色々調べてみました。

大筋

Windows Store Application ならば Frame.Navigate(targetPageClass, parameter) のように遷移用のメソッドで直接パラメーターを渡す事ができ、パラメーターも Page.onNavigatedTo(navigateEventArgs) メソッドの引数から受け取る事ができます*1
引数でパラメーターを受け取ることができるので、どのタイミングでパラメーターが渡って来るのかが非常に分かりやすく、かつスマートです。

しかし、iOSフレームワークにはそういった機能は無いようです。
遷移先のViewControllerにpublicなメンバを作り、遷移前にそこにデータを突っ込んでパラメーターにします。

delegateパターンほど汚い感じはしませんが、うーん、好きじゃないなぁ…


今回は、タップされたセルの番号を次の画面に渡してみます。

環境

手順

昨日の続きです。StoryBoard上にTableViewControllerを配置してあり、次のViewControllerに遷移するSegueを作成済みと仮定します。

移動先をカスタムした View Controller にする

データを受け取るためには、遷移先のViewControllerにパラメーター受け取り用のメンバを作らなければなりません。
そして、テーブルをタップした後に遷移する先のViewControllerが自作のViewControllerであるとStoryBoardに書く必要があります。

という訳で、カスタムしたViewControllerの作成から行ってみましょう。

プロジェクトに、 ViewController を継承したクラスを追加します。
今回は CutsomViewCOntroller という名前にしました。

f:id:kikuchy:20131104224937j:plain

パラメーター受け取りのため、 CustomViewController にメンバを追加します。
今回はテーブルのセルの番号を受け取るので、 NSInteger 型の rowNumber というメンバを追加しました。

"CustomViewController.h" を以下のように編集します。

#import <UIKit/UIKit.h>

@interface CustomViewController : UIViewController
@property NSInteger rowNumber;
@end

CustomViewController をStoryBoard上のViewControllerと関連づけます。
"Main.storyboard" を開き、遷移先ViewControllerを選択して、Identity InspectorのCustom Classの欄に "CustomViewController" と入力します。

f:id:kikuchy:20131104230033j:plain

これでパラメーターを渡す準備はできているのですが、このままではViewは真っ白。
パラメーターが渡ったのかどうかわからないので、Viewをちょっといじります。
ラベルを置いて、そこに選択されたセルの番号を表示してみましょう。

遷移先の CustomViewController の中に適当なLabelを一つ置いて、 "CustomViewController.h" にOutletを作成します。
今回は label という名前で作成しました。

f:id:kikuchy:20131104230504j:plain

受け取ったパラメーターをラベルに設定しましょう。
CustomViewController クラスの rowNumber に受け取ったパラメーターが入っているようにするので、 viewDidLoad のタイミングでラベルの文字を仕込んでやると良いでしょう*2

"CustomViewController.m" の viewDidLoad を以下のように編集します。

- (void)viewDidLoad
{
    [super viewDidLoad];

    // @property で生成したメンバへは、_(メンバ名) でアクセスできる
    _label.text = [NSString stringWithFormat:@"%d", _rowNumber];
}

これで、パラメータが渡っていれば遷移後に行番号が表示されるはず!

パラメーターの受け渡し

それではパラメーターを渡しましょう。
遷移元のViewControllerである TableViewController を編集します。

まず "TableViewController.h" に "CustomViewController.h" をインポートするように指示します。

#import <UIKit/UIKit.h>
#import "CustomViewController.h"

@interface TableViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate>

@end

次に "TableViewController.m" の prepareForSegue:sender: を以下のように編集します。

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"selectRow"]) {
        CustomViewController *vcntl = [segue destinationViewController];    // <- 1
        vcntl.rowNumber = [self.tableView indexPathForSelectedRow].row;    // <- 2
    }
}

prepareForSegue:sender:performSegueWithIdentifier:sender: などで Segue が実行されると、実行直前に自動的に呼び出されるようです。
ただし、どんなSegueが実行される時も呼び出されるので、複数のSegueを定義している場合は identifier を調べて、今実行中のSegueがどのSegueなのかを調べる必要があります。

ここでは、実行された Segue が "selectRow" だったらば、遷移先のViewControllerのインスタンスを取得し(1)、遷移先ViewControllerのメンバーにパラメーターを渡しています(2)。

渡しているのは、テーブル上で現在選択されているセルの番号です。


これで必要な作業は終わりました。
シミュレーターで実行してみると、前回同様数字がずらり。

f:id:kikuchy:20131104232652j:plain

適当に4番なんかをタップしてやると、

f:id:kikuchy:20131104232841j:plain

ちゃんとラベルに行番号が表示されていますね!
もちろん、タップする行を変えると、次の画面で出てくる数字も変わります。



遷移時のパラメーターと言いつつ、オブジェクトのメンバにデータを代入するだけなんですよね。
複雑なオブジェクトもコピーする事無く、直接受け渡しできるので、省メモリと言えば省メモリなのかも知れません。
もちろんその分だけメモリ管理は大変になる訳ですが。

*1:パラメーターは一度シリアライズされるので、基本型以外を渡すのは推奨されていませんが

*2:それ以前のタイミングでラベルに値を入れようとしても、UILabelのインスタンスがまだ生成されていないのでエラると思います