【iOS】私が薦める画面遷移方法

皆さんは画面遷移を定義する時、StoryboardのSegueを使っていますか? 僕はこのSegueというやつがどうも好きになれず、コードで画面遷移を定義しています。

<img width="632" alt="スクリーンショット 2017-02-05 16.28.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/73490/a3faac28-3340-d722-cdb9-1fc229ef4454.png">

上図のように、画面A -> 画面B -> 画面Cとシンプルな遷移のみのアプリならばSegueは良いかもしれません。しかし、本格的なアプリを作っていくと、どうしても画面Aから画面Cへ飛ばないと要件を満たせないといったことがあります。画面数も増えると、Segueの線もタコ足配線のようにゴチャゴチャした感じに引かれ、見辛くなっていきます。
実際の作業面で言えば、13インチMacBookのような狭いスクリーン上で線を引っ張ってViewController同士をつなげる作業はけっこうストレスです…

なので、僕はSegueを使わず、コードで画面遷移させる方法を薦めています。

ただ、Storyboardを完全に使わないというわけではありません。ViewのレイアウトはStoryboardを使った方がやはり楽だと思います。

この記事では画面遷移のみをコードで定義する方法を紹介します。

# Step1: Storyboard上の設定

まずはじめに、`Main.storyboard`にサインイン画面を作っているとします。この画面はユーザが未サインイン状態の時に、サインイン無しでは利用できない画面(例えば、プロフィール画面や購入画面)から呼び出されます。

下図のように、サインイン画面用のViewControllerを選択して、右パネル上の**Custom Class**のClassにこのViewControllerのクラス名を入力します。
例) `SignInViewController`
**Identity**のStoryboard IDには任意のユニークとなる文字列を入力します。
例) `SignIn`

<img width="570" alt="スクリーンショット 2017-02-05 17.09.27.png" src="https://qiita-image-store.s3.amazonaws.com/0/73490/df858f43-7264-4e8d-0968-6466c4df82f5.png">

# Step2: 遷移先のViewControllerの実装

ヘッダファイルには`SignInViewController`クラスのインスタンスを生成するクラスメソッドだけを定義します。このメソッドではNSString型のメッセージを引数として受け取るようにしています。

```objc:SignInViewController.h
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface SignInViewController : UIViewController

+ (instancetype)instantiateViewControllerWithMessage:(NSString *)msg;

@end

NS_ASSUME_NONNULL_END
```

実装は以下のようにします。

```objc:SignInViewController.m
#import "SignInViewController.h"

@interface SignInViewController ()

@property (nonatomic) NSString *msg;

@end

@implementation SignInViewController

+ (instancetype)instantiateViewControllerWithMessage:(NSString *)msg {
    // SignInViewControllerのインスタンスを生成
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    SignInViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"SignIn"];
    
    // 画面遷移アニメーションの設定(お好きなものを)
    vc.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
    
    // 遷移元から渡された値を保持
    vc.msg = msg;
    
    return vc;
}

...

@end
```

`storyboardWithName:`メソッドの引数にはStoryboard名を指定します。`Main.storyboard`なら`Main`。
`instantiateViewControllerWithIdentifier:`メソッドの引数にはStep1で設定したStoryboard IDを指定します。

この方法の気に入っている点は、安易にViewControllerのプロパティを外部に公開しなくて済むことです。readwriteな`msg`プロパティを.hファイルに書かない。
メソッド名から遷移先に渡さないといけない値が何かも理解しやすいです。

# Step3: 遷移元のViewControllerの実装

例えば、購入画面を表示する時、ユーザが未サインイン状態だったのでサインイン画面へ遷移させます。

```objc:BuyViewController.m
...

#import "SignInViewController.h"

...

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    if (/*サインインしていない*/) {
        // サインイン画面へ
        id vc = [SignInViewController instantiateViewControllerWithMessage:@"購入するためにはサインインしてください"];
        [self presentViewController:vc animated:YES completion:nil];
    }
}

...
```

とても簡単です。画面遷移させたいところで2,3行のコードを書くだけです。
上の例はモーダル遷移ですが、NavigationController配下では、

```objc
[self.navigationController pushViewController:vc animated:YES];
```

とすればプッシュ遷移します。

遷移元の画面へ戻る時は、モーダル遷移ならば、

```objc
[self dismissViewControllerAnimated:YES completion:nil];
```

プッシュ遷移ならば、

```objc
[self.navigationController popViewControllerAnimated:YES];
```

を実行すればいいわけです。

いかがでしたか? Segueを使うよりもスマートだと思いませんか。
少しでもiOS開発のご参考になれば幸いです。