StoryBoardで画面遷移、画面間の値の受け渡し(とデリゲート)

| カテゴリ:iPhoneアプリ
XcodeでのiOSアプリ(iPhoneアプリ/iPadアプリ)開発において、StoryBoard(Xcode4.2の新機能である、画面UIの簡単実装)で画面遷移させたいときの方法まとめです。
前半は画面遷移そのもの、後半は画面遷移時の値の受け渡しについて書いています。
可能な限りシンプルに余計なことは書かないようにというモットーでありながらも、デリゲートとかボタンアクションとかのiPhoneアプリ開発で必須となる知識も一緒におまとめのお得記事!
XCodeのバージョンは4.4.1でやっていますが、他のバージョンでもほとんど差異はないかと思います(現時点においては)。
画面遷移にせよ値の受け渡しにせよ、方法は幾通りもあります
本記事はその一例となりますことはご了承くださいませ。

下準備

まずはFile→New→Projectで新規プロジェクトを作ります。

構造がシンプルで説明しやすいし理解しやすいと思うので、Single View Applicationを選択。

Product NameはsegueSampleとしました。
DevicesはiPhoneで。他のデバイスでも手順は変わりません。
Use Storyboardのチェックを入れるのを忘れずに。
Use Automatic Reference Countingはデフォルトのままチェックを入れておきました。
Automatic Reference Counting(ARC)はオブジェクトの管理をOSに任せてしまう機能です。retainやreleaseを記述しなくてよくなります。
これもXcode4.2から追加されました。
ARC使っても使わなくても本記事の本質そのものに変わりはありませんが、もしもARCを使わない場合、記述が変わります。

プロジェクトを生成すると、segueSampleフォルダの中に五つのファイルがあります。
AppDelegate.h、AppDelegate.m、MainStoryboard.storyboard、ViewController.h、ViewController.mの五つです。
AppDelegate.h、AppDelegate.mはアプリ全体に関わる処理を受け取るためのクラスです。
たとえば、アプリが起動したときとか、バックグラウンドに移行したときとか。
今回は変更しません。
MainStoryboard.storyboardが画面構成を記述するファイルです。
これが今回の肝であるStoryBoard。
選択してみると↓のように表示されます。

ViewControllerって名前がついたものが一つだけあります。これがiPhoneで表示される初期画面になります。
(正確には画面に表示されるものはViewで、ViewControllerはそれの管理)
まだなにもしていないので真っ白です。
ViewController.h、ViewController.mはこのViewControllerについてコードを記述するファイルになります。
.hが定義を書くファイル、.mが実装を書くファイルです。
外向きが.h、内向きが.m、みたいな言い方もできるのですが、このあたりの細かい説明は省きます。
iPhoneアプリ開発では、用意されているオブジェクトに手を加えるためには、この名前が同じで拡張子が異なるhとmの一対のファイルを新たに作っていくことになります。
Single View Applicationの場合、初期画面に関するクラス(ViewController.hとViewController.m)は自動で作られます。
では、StoryBoardに二つ目の画面を置いてみます。

Objects(画像の赤く囲んだ部分にあります)からView Controllerをドラッグアンドドロップすると上記画像のようになります。
位置はご自由にどうぞ。
これで下準備は完了です!

一つ目の画面から二つ目の画面へ遷移


まず、一つ目の画面上にRound Rect Buttonをドラッグアンドドロップしてください。
わかりやすくするため、ボタンをダブルクリックして「遷移」と書いておきました。

controlを押しながら今置いたボタンをクリックし、そのままマウスカーソルを二つ目の画面に引っ張るとボタンクリック→画面遷移という流れが実現できるのですが、今回はこの手は使いません。
ボタンクリック以外での画面遷移にも今後対応できるよう、ちょっと遠回りですがボタンクリック→それを検知→画面遷移というステップを踏むことにします。

一つ目の画面の下にあるView Controllerのアイコンをcontrolを押しながらクリックし、そのままマウスカーソルを二つ目の画面に引っ張ってください。
すると上の画像のように線が伸びます。
マウスから指を離すと"Push"、"Modal"、"Custom"のいずれかを選ぶようにポップアップが出ます。
今回はModalを選択します。
(元画面の上に新画面を表示する方式)

上の画像のように一つ目の画面から二つ目の画面に矢印が描かれます。
この矢印をクリックして、Identiferのところに画面遷移の名称を書きます。
私はmySegueとしました。
他の名称にするときは、以降のコードでもそれに合わせてください。
ちなみにProduct Nameにも出てきましたSegueという単語は切れ目なく移行するという意味です。
iPhoneアプリ開発では画面遷移をSegueと呼びます。

Editorの表示を三つ並んだうちの真ん中Show The Assistant Editorにすると、Xcode上に二つの作業領域が表示されます。
左側はStoryBoardのまま、右側でViewController.hを表示します。
そして、controlを押しながらボタンをクリックし、そのままマウスカーソルを@interface ViewController : UIViewControllerの下まで引っ張ってください。

ConnectionでActionを選んで、NameにpushBtnと入れ、Connectを押します。
このNameは、ボタンをタップした際に呼び出されるメソッド名となります。
他の名称でも構いません。

ViewController.hに- (IBAction)pushBtn:(id)senderというメソッドが記述されました。
ボタンを押すとこのメソッドが呼び出されます。
次にViewController.mを開きます。
こちらにも- (IBAction)pushBtn:(id)senderというメソッドが記述されています。
hに書き込まれたのが定義、mに書き込まれるのが処理です。
つまり、このmの側でメソッドになにをさせるかを書くことになります。

上の画像のように一行追加してください。
追加したコードは下記。
[self performSegueWithIdentifier:@"mySegue" sender:self];
self(つまり自分)がmySegue(さっき自分でつけた名前)を使って遷移しますという意味の処理です。
画面遷移が複数あるときも、Identifierで分岐可能です。
これで一つ目の画面から二つ目の画面への遷移は完成です!

二つ目の画面を閉じる

次に、二つ目の画面を閉じて一つ目の画面に戻る処理を書きます。
左側にあるProjectNavigaterでメニューを表示して

NewFileを選択。

Objective-C classを選択。

名前は二画面目ってことで、SecondViewControllerとしました。名前は自由ですが、わかりやすいものにしましょう
Subclass ofはUIViewControllerにしてください。こちらは自由ではありません。
これでUIViewControllerを継承したSecondViewControllerというクラスが作られます。
継承というのはクラスの内容を受け継ぐことです。

はい、作られました。SecondViewController.hとSecondViewController.m。

StoryBoardを表示して二つ目の画面を選択し、ClassをSecondViewControllerにしてください。
これでこの画面はSecondViewControllerのインスタンス(クラスを元としたデータ)となります。

二つ目の画面にRound Rect Buttonをドラッグアンドドロップしてください。一つ目の画面のときと同じです。
こちらはボタンを「閉じる」としました。

左側にStoryBoard、右側にSecondViewControllerを映します。
そして、controlを押しながらボタンをクリックし、そのままマウスカーソルを@interface ViewController : UIViewControllerの下まで引っ張っります。
一つ目の画面と同じ手順です。

ConnectionでActionを選んで、NameにpushBtnと入れ、Connectを押します。
これも一つ目の画面のときと同じ。

SecondViewController.mを開き、pushBtnメソッドの中にコードを書きます。
[self dismissModalViewControllerAnimated:YES];
dismissModalViewControllerAnimatedはmodalで表示した画面を閉じるメソッドです。
画面遷移はこれで完成です!

一つ目の画面と

二つ目の画面がしっかり遷移します。

一つ目の画面から二つ目の画面へ値を渡す


まずSecondViewController.hにNSString* _myValue;@property (nonatomic)NSString* myValue;の二行を追加します。
↓のように。
@interface SecondViewController : UIViewController{
    NSString* _myValue;
}

@property (nonatomic) NSString* myValue;
追加した一つ目は_myValueという名の文字列インスタンスを宣言しています。
二つ目はmyValueという名の文字列のプロパティを宣言しています。
プロパティとは、端的に言えばクラスを外部から扱いやすく設定すること、です。説明すると本筋から離れすぎるので、詳しくは検索してください。
_myValueとmyValueは別物なの?
なんで_がついてるの?
と思うかもしれません。
後述するsynthesize文により、二つは結びつけられるようになります。
どうせわざわざ結びつけるなら、なんで別の名前にするの?
最初から同じにしておけばいいんじゃない?
これはわざわざ_をつけることでどういう用途のものかを明示的にし、バグを防ぐためです。
一つの文法マナーです。守らなくてもプログラムは動きます。
詳しくは検索してみてください。ちなみに、つけるべきではないって人もけっこういます。

続いて、SecondViewController.mに@synthesize myValue = _myValue;を追加します。
この一文によって、myValueと_myValueが結びつけられます。myValueが変わると、_myValueも変わります。
@synthesize myValue = _myValue;

次に、ViewController.hを開き、#import "SecondViewController.h"の一文を追加します。
こうすることによって、ViewControllerがSecondViewcontroller.hで定義されているものにアクセスできるようになります。
#import "SecondViewController.h"
この段階ではViewController.mの側に記述しても動作します。
二つ目の画面から値を受け取るときのため、h側に書いておいてください。
ViewController.hに書いた内容はViewController.mでもアクセスできます(mがhをimportしているため)。

次に、ViewController.mに新たにprepareForSegueというメソッドを記述します。
↓のコードをまるまるコピーアンドペーストしてください。
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"mySegue"]) {
        SecondViewController *viewCon = segue.destinationViewController;
        viewCon.myValue = @"test";
    }
}
prepareForSegueは画面遷移が行われる前に呼び出されるメソッドです。
if文で画面遷移がmySegueかどうかをチェックしています。
画面遷移が複数のときは、こう書くことで遷移先によって前処理を分岐できます。
if文の中には、遷移先画面インスタンスの取得と、遷移先画面のmyValueへの値の設定をしています。
myValueはさきほどSecondViewController.hにプロパティとして記述したものです。
testという文字列にしたことに意味はありません。
今回はNSString型にしていますが、他の型でもやり方は同様です。
これで二つ目の画面の_myValueにtestという値がセットされました!

SecondViewController.mのpushBtnの中にNSLogを書いてみました。
NSLog(@"%@" , _myValue);//渡された変数の表示
これで閉じるボタンを押した際に値を確認できます。

二つ目の画面から一つ目の画面へ値を渡す

まず、デリゲートというものについて簡単に説明。
デリゲートとは日本語で言うと委譲です。
デリゲートを使うことによってインスタンスAで起きたことをインスタンスBで処理させるといったことができるようになります。
なんかややこしいですが、基本的な記述ルールさえ覚えてしまえば機械的に対応できるようになります。
今回は二つ目の画面が閉じるとき、それをデリゲートによって一つ目の画面に通知する。そのときに値を渡す。という処理を組み込んでみます。

まず、SecondViewController.hです。
追加されたのは三箇所。
まず、↓のコード。
@protocol SecondViewDelegate
- (void)finishView:(NSString*)returnValue;
@end
これはSecondViewDelegateというデリゲート名でfinishViewというデリゲートメソッドを定義したことを意味します。
NSStringを渡すという定義にしていますが、これは他の型でも変わりません。
次に、↓の二つ、各一行。
id _delegate;
@property (nonatomic) id delegate;
このdelegate(_delegate)が通知先を入れるためのインスタンスになります。
通知先は、通知を受ける側でセットされます。
今回の場合は一画面目であるViewControllerです。
続きましてSecondViewController.mです。

まず、上の方(9行目)に↓のコードがあります。
delegateと_delegateを結びつけています。
@synthesize delegate = _delegate;
次に、pushBtnメソッドの中に以下の三行が追加されています。
if ([_delegate respondsToSelector:@selector(finishView:)]){
    [_delegate finishView:@"finish"];
}
if文において、通知先が存在しているかどうかを確認しています。
その上で通知先のfinishViewメソッドを呼び、その際にfinishという文字列を渡しています。
finishという文字列そのものに意味はありません。適当です。
これで通知元であるSecondViewControllerの記述は完了です。
次に、一つ目の画面である、ViewController.h。

<SecondViewDelegate>という記述が加えられています。
@interface ViewController : UIViewController
これはSecondViewDelegateを受けるという宣言です。
この宣言のために#import "SecondViewController.h"の一文をmではなくhに書きました。
mに書いてしまうと、hではこれが何者かわからないのです。
最後に、ViewController.m。

prepareForSegueの中に以下の一文が追加されています。
viewCon.delegate = self;
delegateというのは先程SecondViewControllerで記述したプロパティです。
このプロパティにself、つまり自らを入れることで、デリゲートの通知先になれます。
実際に呼ばれるのが以下のメソッドです。
- (void)finishView:(NSString*)returnValue{
    NSLog(@"%@" , returnValue);
}
finishViewもSecondViewControllerで先程記述しましたね。
returnValueの中に、SecondViewControllerから渡される値(今回はfinish)が入っています。
NSLogは受け取った値の確認用で入れておいたコードです。

終わり

かなり長文になってしまいましたが、その分だけ、画面遷移のみではなく、iPhoneアプリ開発で必要となる重要な要素をいくつも詰め込んだつもりです。
意見等ありましたら、Twitterででも気軽に声かけてください。

概要

青春B運営メンバー多口カタンによる雑記blogです。
自己紹介はこちら。開発物をまとめたものはこちら
 
ヘッダーイラストはkojiさん制作です。
感想・意見・要望等ありましたら気軽にフォームにてコンタクトくださいませ。
 
Twitterはじめましたので誰でも気軽に声かけてくださいね。