Kesin's diary

プログラミングの記事がメインです

StoryboardとSegueの基本

Xcode4.2からStoryboardという画面(ビュー)のレイアウトと別の画面への遷移をコードを書かずにグラフィカルに作成することができる機能が追加されました。自分はXcode4.4からiPhoneアプリの開発を始めたので、昔はどのように画面のレイアウトや遷移を設定していたのか分からないのですが、Storyboardの基本的な部分で何度もハマったのでこれからiPhoneアプリの開発を始める方のために自分が苦戦したポイントのメモを残そうと思います。



執筆時ではXcode4.4.1でARCを使うコードとなっています。iOS SDKXcodeの進化は速いのでこのバージョンより新しい(古い)Xcodeではこの記事の通りにやっても動かない可能性があります。ご注意ください。

1. ビュー遷移の設定

まずは最も基本的なボタンを押した時のビュー遷移の説明です。あまりに簡単なのでサクッと説明してしまいますと、Storyboard上でビュー上に配置したボタンを右クリックしながら遷移させたいビューまでドラッグして接続するだけです。このときにpush, modal, customの3つの遷移の仕方が選べます。pushは次のビューが右から左に出てくるエフェクトで、modalは下から上に出てくるエフェクトです。customは自分でこのエフェクトを作成するときに使うようです(使ったことがないので詳しいことはわかりません)


もう少し複雑な遷移を行いたい場合、例えばボタン以外をトリガーとしたビュー遷移や、条件によって異なるビューへ遷移させたい場合は手動でsegueを選んで遷移させることも可能です。その場合、今度はビュー上のボタンではなくて単にビューからビューに接続してsegueを作成します。そしてこれが重要なのですが、segueをクリックしたときユーティリティウィンドウからsegue identifierにsegueの名前を設定します

これは後でコードを書くときにsegueを特定する名前になるので、次のビューで何を行うかなどの分かりやすい名前を付けることをオススメします。
ここまで設定できたら次はビューのコード上でボタンに対応したアクションやテーブルビューのアイテムを選択したときなどのビューを遷移させたいタイミングの場所に

[self performSegueWithIdentifier:@"segue identifier" sender:self];

と書くだけです。一つ目の引数である文字列が先ほど設定したsegueのidentifierに対応するので、何か処理をしたときにsegueを振り分けたいといった場合はif文で簡単に実現できます。

2. 前のビューに戻る

さて、ビュー遷移ができるようになったのはいいですが、このままでは一方通行でビュー遷移をした後に戻ることができません。これでは非常に困りますね。
NavigationControllerを設定していれば自動的に上部のナビゲーションバーに"戻る"ボタンが作成されているかと思いますが、任意のタイミングで前のビューに戻りたいこともあります。実はこれは非常に簡単で、pushで遷移した場合は遷移先のビューで

[self.navigationController popViewControllerAnimated:YES];

を呼び出すだけです。modalの場合は

[self dismissModalViewControllerAnimated:YES];

になります。
ビュー遷移の実装方法は色々あるみたいで、以下のリンクではデリゲートを用意して遷移元のビューが遷移先から戻したり、モーダルを閉じたりさせる方法が紹介されています。このあたりは使いやすいと思った方法を選べばいいと思います。
http://ssdkfk.wordpress.com/2011/07/07/modalview%E3%81%8C%E9%96%89%E3%81%98%E3%82%8B%E3%81%93%E3%81%A8%E3%82%92%E8%A6%AA%E3%83%93%E3%83%A5%E3%83%BC%E3%81%AB%E4%BC%9D%E3%81%88%E3%82%8B/

3. ビュー間で変数の受け渡しを行う

自分がiPhoneアプリ開発で一番ハマったのは実はこのポイントです。ビュー間で変数の受け渡しを行いたいというケースは非常に頻繁にあると思いますが、最初はどうやっていいのか全然分かりませんでした。このビュー間の変数の受け渡しはたくさんの方法があるようですが、Storyboardを使用しているならば以下のようにprepareForSegueの中で次に遷移するビューの変数に代入するのが簡単です。

NextViewController.h

#import <UIKit/UIKit.h>

@interface NextViewController : UIViewController
//別のクラスから参照したい変数をpropertyに宣言しておく
@property NSString *receiveString;

- (IBAction)backView:(id)sender;
@end

NextViewController.m

最新のXcodeなら.hにpropertyで定義したものは.mで改めて宣言する必要はないようです

viewController.m

#import "ViewController.h"
#import "NextViewController.h" //遷移先ビューのクラスをimportしておく

@interface ViewController ()

@end

@implementation ViewController{
    NSString *sendString;
}

- (IBAction)nextView:(id)sender {
    [self performSegueWithIdentifier:@"next" sender:self];
}

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    //Segueの特定
    if ( [[segue identifier] isEqualToString:@"next"] ) {
        NextViewController *nextViewController = [segue destinationViewController];
        //ここで遷移先ビューのクラスの変数receiveStringに値を渡している
        nextViewController.receiveString = sendString;
    }
}
@end

*説明に不要なViewDidLoadなどのメソッドは記述を省略しています


prepareForSegueはSegueはビューが遷移するタイミングで呼ばれるもので、[segue destinationViewController]よって遷移先のビューの参照を持ってきて変数を代入をするというコードになっています。elseやelse ifを加えればSegueによって処理を分けるということも可能です。

上のように遷移元(親)が遷移先(子)に変数を渡すだけならこれだけで済むのですが、実際には逆方向の子から親へ変数を渡したかったり、そもそも親子関係の無いようなビュー間で変数を受け渡したいケースは割りと多いです。そのような場合は自分でデリゲートを作ったりとか色々な方法があるようで、以下のサイトにケース別に解説があります。
http://eien.seesaa.net/article/261740269.html
けど本ブログを参考にしているレベルぐらいだと上のサイトはちょっと難しいかもしれません(というか自分がそうでした)。その場合、小規模なアプリならば全てのビューからAppDelegateに用意した変数にアクセスするという方法がおそらく最も簡単です。

AppDelegate.h

//全てのビューから参照する変数
@property NSString *shareString;


ViewController.m (AppDelegateに用意した変数を参照するビューのクラス)

#import "AppDelegate.h"

AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
appDelegate.shareString = @"Hello World";

しかしこの方法だとアプリが大規模になってAppDelegateに似たような名前の変数が増えてくると段々収集がつかなくなってくるので、慣れてきたら別の方法の方が良いと思われます。