StoryboardのUnwind Segueの使い方
Exitという名前の謎の緑アイコン
Xcode 4.5から、Storyboardに「Exit」という名前の緑のアイコンがシーンに追加されました。当初調べた際には、セグエで繋いでいる画面を戻るための機能(Unwind)であることは分かったのですが、具体的な利用方法が分からずもやもやしていました。最近になってようやく役割と利用方法が分かったのでまとめたいと思います。
Unwind Segue
iOS 6になってUnwind Segueという新しいセグエが追加されました。UIViewControllerには、これをサポートするためのcanPerformUnwindSegueAction:fromViewController:withSender:などといったメソッドが追加されています。
Unwind Segueは前の画面に戻る遷移をStoryboard上で表現するためのセグエです。それとともに、今までデリゲートなどを利用して実現していた、遷移先画面から遷移元画面に戻る際のデータ受け渡しなどの処理を実装しやすくする仕組みを提供しています。具体的には、Unwind Segueは戻り先画面と遷移終了後に戻った先の画面で実行するアクションを定義します。
サンプルを作成しながら詳細について見ていきます。環境は以下の通りです。
- OS X Lion
- Xcode 4.5.1
- Deployment Target: iOS 6.0
StoryboardでUnwind Segueを設定
Xcodeで新規プロジェクトをSingle View Applicationで作成します。NavigationControllerをInitialViewControllerに設定します。ViewControllerを3つ追加してそれぞれにカスタムUIViewControllerクラスを割り当てた後、Pushセグエで繋ぎます。
ではまず、3番目のビュー(緑色)から1番目のビュー(オレンジ色)に戻るUnwind Segueを追加したいと思います。3番目のビューに遷移のトリガとなるUIButtonを追加します。ボタンを追加したら、3番目のビューのシーンから"Exit"を選択してUtilitiesペインのConnections inspectorを表示して下さい。この時点では何も表示されていません。
Unwind Segueを追加できるようにするためには、戻り先の画面にアクションメソッドを定義する必要があります。Unwind Segueのアクションメソッドはシグネチャのみが決まっており、UIStoryboardSegue型のポインタを引数に取ってIBAction型を返すメソッドです。
戻り先画面は1番目のビューにしたいので、1番目のビューのViewControllerに以下のメソッドを定義します。
- (IBAction)firstViewReturnActionForSegue:(UIStoryboardSegue *)segue { NSLog(@"First view return action invoked."); }
もう一度3番目のビューのシーンから"Exit"を選択してUtilitiesペインのConnections inspectorを表示して下さい。Connections inspectorにPresenting Seguesという項目が追加され、先ほど定義したアクションメソッドが表示されていると思います。
このアクションメソッドを画面遷移用に追加した3番目のビューのボタンに接続します。
すると、下図のように3番目のビューのシーンにUnwind Segueが追加されます。
Storyboardのキャンバス上にはコネクタが表示されませんが、これで3番目のビューのボタンをタップした際に1番目のビューに戻る処理がUnwind Segueによって定義されました。
Unwind Segue実行時に画面間で必要な処理をする
先ほど設定したUnwind Segueは実行時に設定したアクションメソッドを呼び出します。Unwind Segueの実行時には以下の順で処理が行われます。
- 遷移元ビュー(現在表示中のビュー)のViewControllerのcanPerformUnwindSegueAction:fromViewController:withSender:メソッドの呼び出し
- 遷移元ビューのViewControllerのprepareForSegue:sender:メソッドの呼び出し
- Unwind Segueで設定された遷移先ビューのアクションメソッドの呼び出し
- セグエ実行
2番目に呼ばれるprepareForSegue:sender:は通常のセグエ実行前に呼ばれるメソッドと同じものです。prepareForSegue:sender:とアクションメソッドのパラメータで渡されるUIStoryboardSegueは遷移元のViewControllerと遷移先のViewControllerの参照を持っているので、このどちらかのメソッド呼び出し時に画面間のデータの受け渡しなどの処理を記述することができます。
prepareForSegue:sender:は遷移元のViewControllerに、アクションメソッドは遷移先のViewControllerに定義されたものが呼び出されます。このため、場面に応じて一方のViewControllerからもう一方のViewControllerを参照してプロパティなどにアクセスすることができます。通常は遷移先のViewControllerから遷移元のViewControllerを参照してデータを取り出すのがいいかと思います。
prepareForSegue:sender:もアクションメソッドも複数のセグエから呼び出される可能性がありますので、どのセグエが実行されて呼び出されたのかを判別する必要があります。Unwind Segueも通常のセグエと同様にidentifierを設定することができるので、これを利用して通常のセグエ利用時と同じように切り分けることができます。
- (IBAction)firstViewReturnActionForSegue:(UIStoryboardSegue *)segue { if ([segue.identifier isEqualToString:@"BackToFirstViewFromLastViewSegue"]) { // ここに必要な処理を記述 NSLog(@"Back to first from last."); } NSLog(@"First view return action invoked."); }
Unwind Segueのidentifierは、Storyboard上のシーンからindentifierを設定したいUnwind Segueを選択してAttributes inspectorを開くと、Storyboard Unwind SegueセクションにIdentifierという項目がありますので、そこから設定できます。
なお、最初に呼ばれるcanPerformUnwindSegueAction:fromViewController:withSender:メソッドは、Unwind Segueによる画面遷移を実行するかどうかを判定して真偽値で返します。UIViewControllerの実装は、このViewControllerのparentControllerもしくはコンテナコントローラ(たとえばUINavigationController)の子が、1番目のパラメータで渡されているセレクタのメソッドを持っているかを判定し、該当するメソッドがある場合にYESを返すようになっています。parentControllerに関しては、メソッドを持っていないと判定された場合、さらにそのparentControllerがメソッドを持っているかチェックするといった形でメソッドが見つかるまでparentControllerを辿ります。
プログラムからUnwind Segueを実行する
Unwind Segueは通常のSegueと同様にUIViewControllerのperformSegueWithIdentifier:sender:メソッドを利用してプログラムからマニュアル実行することもできます。では、3番目のビュー(緑色)から2番目のビュー(黄緑色)に戻るUnwind Segueを設定して、それをマニュアル実行してみましょう。
Storyboard上でUnwind Segueを設定する手順は先ほどとほぼ同じです。まず、2番目のビューのViewControllerに以下のUnwind Segue実行時に呼び出すアクションメソッドを定義します。
- (IBAction)secondViewReturnActionForSegue:(UIStoryboardSegue *)segue { NSLog(@"Second view return action invoked."); }
次に、3番目のビューのシーンから"Exit"を選択してUtilitiesペインのConnections inspectorを表示すると、先ほど定義したアクションメソッドが表示されているので、3番目のビューのViewControllerに接続します。接続しようとすると"manual"と書かれたツールチップが出現するので、選択します。シーンから作成したUnwind Segueを選択して、identifierを割り当てておきます。
最後に、3番目のビューにUnwind Segueのトリガとして使用するボタンを配置し、ViewControllerにTouchUpInsideのアクションに紐付いたアクションメソッドを作成します。作成したアクションメソッドでperformSegueWithIdentifier:sender:メソッドを呼び出します。
- (IBAction)manualUnwindButtonDidTouch:(id)sender { [self performSegueWithIdentifier:@"ManualUnwindSegue" sender:self]; }
これで3番目のビューから2番目のビューにUnwind Segueをマニュアル実行して戻ることができるようになりました。
Unwind Segueでモーダルビューを閉じる
これまではUINavigationControllerによる遷移をUnwind Segueで戻る方法を見てきましたが、Unwind Segueはモーダルビューのクローズ操作もサポートしています。今までdismissViewControllerAnimated:completion:メソッドを呼び出していた部分(dismissViewControllerAnimated:メソッドはiOS 6からdeprecated)をUnwind Segueで定義することができることになります。それだけでなく、「モーダルビューを閉じてさらに1つ前のビューに戻る」といった処理も一つのUnwind Segueで表現することができます。
実際にモーダルビュー(水色)から1番目のビュー(オレンジ色)に戻るUnwind Segueを設定してみました。Unwind Segueの設定手順は全く同じです。Unwind Segueを実行すると、モーダルを閉じた上で1番目のビューが表示されます。
まとめ
Unwind Segueの導入によってStoryboardでできる画面遷移の定義が広がり、画面遷移とその際の処理の見通しが良くなりました。また、今回は触れませんでしたが、iOS 5で追加されたchildViewControllerの追加処理も、Xcode 4.5以降のStoryboard上ではContainerViewコンポーネントとしてサポートされています。(こちらもiOS 6以降でのみ利用できます。)ターゲットになるOSがiOS 6以降であるアプリの場合は積極的に利用したいですね。
今回のサンプルのソースコードはGitHubにアップしてあります。