iOSアプリで画面遷移(モーダルなビューでデリゲート)

Google先生に「iOS 画面遷移」という場合、期待している答えにはたどり着けない。

それは多分、「モーダルでビューを開く」ということになる。一覧から明細をタップしたら切り替わるのは、「Master-Detail」とかいわゆるUINavigation系の話で、別の話。別の話なのだよ!!。ここでは「モーダル」なやつについてメモっておく。


モーダルでビューを開く想定例アプリ

  • とある画面Aにボタンがある
  • ボタンをタップしたら別の画面Bが「ぴょ」とでてくる
  • 画面Bには「閉じる」的なボタンがある
  • 画面Bの「閉じる」を押したら、画面Bがひっこんで画面Aに戻る


これだけのことがやりたいのだが…!!

大原則

  • UIViewControllerのpresentModalViewController で開く
  • UIViewControllerのdismissModalViewControllerAnimated で閉じる

だがしかし

  • 画面Bを開くときは、UIViewControllerのpresentModalViewControllerを使う
  • 画面Bを閉じるときは、画面BでdismissModalViewControllerAnimatedはしない

だそうだ。

ここにdelegateという考え方がでてくる。そんな回りくどいことしたかねえよというかもしれないが、もし画面Bで入力した値を画面Aで使いたいとしたら、(保存しちゃうという手もあるけど)デリゲートを使ったほうがよいのあるらしい。

あらすじ

  • 画面Bのヘッダーファイルに、画面Bデリゲートをつくりメソッドを定義する。プロパティに自分のデリゲートを追加する。
  • 画面Aは画面Bデリゲートのメソッドを実装する。そこで画面Bを閉じる記述をする。
  • 画面Bを開くときは、UIViewControllerのpresentModalViewControllerを使うが、そのときに、画面Bのデリゲートに画面Aはselfを設定する。
  • 画面Bを閉じたい箇所で、画面Bは自身のデリゲートのメソッドを実行する。すると結果的に、画面Aのメソッドが実行されるので画面Bは閉じられる。

「実装」「設定」「メソッド」とか言葉があっているのかわからない。
よく忘れるのは、「画面Bのデリゲートに画面Aはselfを設定する」ところ。これを忘れていると、スカスカなにもおこらない。


画面BをInfoViewControllerとしまして、InfoViewController.hの中身

#import <UIKit/UIKit.h>
@class InfoViewController;//プロトコルにわかってもらうために要るらしい

// デリゲート
@protocol InfoViewControllerDelegate <NSObject>
-(void)infoViewController:(InfoViewController*)infoViewController okButtonTapped:(id)sender;
@end

// 本体
@interface InfoViewController : UIViewController

//デリゲート用  retainじゃなくてassign
//@property(nonatomic,retain)id<InfoViewControllerDelegate> delegate;
@property(nonatomic,assign)id<InfoViewControllerDelegate> delegate;

// 画面Bの見た目上の閉じるボタン用
-(IBAction)okButtonTapped:(id)sender;

@end

InfoViewController.m(一部)

@synthesize delegate; //これもよく忘れる。この解放についてはまじよくわかんないぉ。ARC前ならnilとかいれちまって終わっているのか?な? ARCだとうんにゃらかんにゃら。

-(void)okButtonTapped:(id)sender{
    NSLog(@"InfoViewControllerokButtonTapped...");
//    [self dismissModalViewControllerAnimated:YES]; //ここでこれを書くと閉じるけどやらない
    // デリゲートのメソッドを実行する
    [self.delegate infoViewController:self okButtonTapped:sender];
}

画面AにあたるViewController.h

#import <UIKit/UIKit.h>
// デリゲートのためにこれがいる
#import "InfoViewController.h"

@interface ViewController : UIViewController<InfoViewControllerDelegate>
-(IBAction)showInfo:(id)sender;
@end


ViewController.m(一部)

// 画面Bを開く
-(void)showInfo:(id)sender{
    InfoViewController*infoViewController
    = [[InfoViewController alloc] initWithNibName:@"InfoViewController" bundle:nil];
    // ここを忘れるとスカスカ
    infoViewController.delegate = self;
    // 画面Bを開く(開くっていうのか? 違うんだろうなあ)
    [self presentModalViewController:infoViewController animated:YES];
}

// デリゲートのメソッド
-(void)infoViewController:(InfoViewController *)infoViewController okButtonTapped:(id)sender{
    // ここで閉じられる
    // このselfは感覚的には画面Aではなく画面B。なぜかというとデリゲートだから
    [self dismissModalViewControllerAnimated:YES];
}

なんだかもやもや。

単純に閉じるのなから、親から閉じるもあるっぽいし。。。