#2 画面の遷移 - はじめてのSwift UI

#1.はじめに
アプリと言ったら画面遷移については避けて通ることができません。この辺りの使用がUIKitと結構違うなあと思ったので書いていきます。

また、2020/1/26時点での記事ですのでご了承ください。

#2.初期画面の設定
画面遷移のやり方を説明する前にまず初期画面の設定方法を書いていきます。

UIKitではStoryboard上で「Is Initial View Controller」から設定していましたが、SwiftUIの場合は「SceneDelegate.swift」上で設定します。

SwiftUIでプロジェクトを作成すると以下のような「SceneDelegate.swift」が作られていると思います。

```Swift:SceneDelegate.swift
import UIKit
import SwiftUI

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    ...省略...

}


```

上記ファイル内の以下の箇所でcontentViewに設定したViewクラスが初期画面になります。

```Swift:SceneDelegate.swift
let contentView = ContentView()
```

例えば別途Viewを継承したSwiftUIViewクラスを作成していた場合は、以下のように設定することが可能です。

```Swift:SceneDelegate.swift
let contentView = SwiftUIView()
```

#3.画面遷移
##3-1.画面遷移(不具合報告あり？)
画面遷移を説明していきたいのですが、基本となる(であろう)方法を紹介します。※実用的な方法を知りたい方は3-2に進んでください。

```Swift:SceneDelegate.swift
struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination:
                    NextView()) {
                    Text("遷移する")
                }
            }
        }
    }
}


struct NextView:View{
    var body: some View {
        Text("遷移に成功しました")
    }
}
```

これはContentViewからNextViewに遷移する場合の例です。
具体的には以下の部分で繊維の処理を行っています。

```Swift:SceneDelegate.swift
        NavigationView {
            VStack {
                NavigationLink(destination:
                    NextView()) {
                    Text("遷移する")
                }
            }
        }
```

NavigationView→VStack→NavigationLinkと3段の入れ子構造になっています。VStackはHStackでも問題ないですが、とりあえずこのイメージを暗記しましょう。

VStack(HStack)を入れない場合、コンパイルエラーにはなりませんが正しく画面が遷移しなくなります。(なんでだろう。)

NavigationViewの引数Distinationに遷移先のViewクラス、{}内には画面遷移するリンクになるものをおきます。(基本はTextで問題ないかと思いますが他にも試してみてもいいかもしれません)

これで、「遷移する」テキストをクリックすると「NextView」に遷移する動きを実装できました。。。が、しかし！

このNavigationLink、Defaultだと遷移先で「Back」ボタンが表示され、前の画面に戻ることができるのですが、再度同じNavigationLinkをクリックすると遷移できません。
異なるNavigationLinkであれば遷移可能ですが、一度でも「戻った直後にまた同じ遷移先に遷移」しようとすると、異なるNavigationLinkすらも動かなくなります。

あまり詳しく調べていませんが、多分不具合です。
直るのを待つか、現時点ではこの「Back」ボタンを使わない実装にするのがベターのように思います。

##3.2 DefaultのBackボタンを使わない画面遷移
DefaultのBackボタン(NavigationBarBackButton)がうまく使えないみたいなので、改めて画面遷移を作りました。それがこちら。


```Swift:SceneDelegate.swift
struct ContentView: View {
    @State private var showAfterView: Bool = false
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination:
                Button(action: {self.showAfterView = false}){
                    Text("遷移元に戻る")
                }.navigationBarBackButtonHidden(true),isActive: $showAfterView) {
                    Button(action: {self.showAfterView = true}){
                        Text("遷移する")
                    }
                }
                
            }
        }
    }
}
```

「あれれ〜？Viewが1つしかないぞ〜?」

そうなんです。これ、よく見るとわかりますが、一つのViewクラスの中に遷移先のView構造も含まれています。

また3階層以上の画面遷移は今挑戦中ですが一筋縄ではいかなさそうです。

誰か偉い人、早くBackボタンを直してください。

とまあ、怒りの感情は置いておいて
簡単にコードの説明をします。

```Swift
@State private var showAfterView: Bool = false
```
これは、2つの画面のうちを表示するか判断するために使用します。
ざっくりとした説明になりますが、こういったViewの切り替え(更新)に使用する変数には@Stateを付けておく必要があるようです。

```Swift
var body: some View {
        NavigationView {
            VStack {
                NavigationLink(
```

この辺は3-1でも説明しましたが、暗記しましょう。
次にNavigationLink周辺を説明します。

```Swift
NavigationLink(destination:
                Button(action: {self.showAfterView = false}){
                    Text("遷移元に戻る")
                }.navigationBarBackButtonHidden(true),isActive: $showAfterView) {
                    Button(action: {self.showAfterView = true}){
                        Text("遷移する")
                    }
                }
```

destinationに入れる値を画面遷移フラグをFalseにするButtonにしています。VStackやText等、遷移先のView構造をここに書いても大丈夫なのです。
そして、navigationBarBackButtonをtrueにすることで遷移先のBackボタンを非表示にできます。

また、isActive引数にBool値を設定し、Trueになった時にそのNavigationLinkが動くようにしています。

{}内にはナビゲーションリンクにするViewを指定できますが、ここでは画面遷移フラグをTrueにするButtonを指定します。

#おわりに
とりあえず現時点での画面遷移をざっと書きました。
3-1で問題なく動くようになれば不要になる記事なので、とりあえずざっと。
わかりにくい場合はSwiftのアップデートを待ちましょう。