swiftUIで画面回転に対応してみる

## やってみたこと
- 回転によるレイアウト変更時にアニメーションを入れてみる
- 画面回転の対応

![ssss.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184634/7f3c3ed9-3311-6311-67c5-99aa5f4a7f05.gif)

## まずレイアウト変更時のアニメーションから

<img width="200" alt="スクリーンショット 2019-09-04 12.28.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184634/03cb2dfc-948b-8dec-6c40-5f0a7e8d2e82.png">

``` swift
VStack {
  Text("Top And Left")
    .padding(50)
  Text("Bottom And Right")
    .padding(50)
}
```

まずこんな感じのレイアウトを用意しました。

次は、VStackとHStackを用意して

- 端末が縦: VStackで表示
- 端末が横: HStackで表示

にしたいと思います。

まずは試しに縦横の変数を用意して、ボタンで切り替えてみたいと思います

とりあえず横縦の管理は

``` swift
@State var isPortrait = true
```

これで行います。

続けて、フラグを切り替えるためのテスト用のボタンを配置しました。

``` swift
VStack {
    if isPortrait {
        VStack {
            Text("Top And Left")
                .padding(50)
            Text("Bottom And Right")
                .padding(50)
        }
    } else {
        HStack {
            Text("Top And Left")
                .padding(30)
            Text("Bottom And Right")
                .padding(30)
        }
    }
    Button(action: {
        self.isPortrait.toggle()
    }) {
        Text("回転したとき")
    }.padding(.all)
}
```

これでボタンをタップしたらisPortraitが切り替わり、VStackとHStackの表示が切り替わります。

いよいよ最後のステップ、アニメーション追加です。

``` swift
Button(action: {
    withAnimation {
        self.isPortrait.toggle()
    }
})

```

withAnimationを追加するだけで、アニメーションしてくれます。

アニメーションをカスタマイズしたい場合は、VStackとHStackに `transition` でアニメーションを指定するだけです。
[swiftui animation tutorial](https://developer.apple.com/tutorials/swiftui/animating-views-and-transitions)

とても細かいところまでこだわろうとすると色々理解する必要がありますが、
何も知識がなくても簡単にできちゃうのはすごいですね。

## 画面の回転を検知してみる

参考にしたのは[stackoverflow](https://stackoverflow.com/questions/57441654/swiftui-repaint-view-components-on-device-rotation)です。

まずは縦横情報をObservableObjectにしたいので、modelを作成します。

``` swift
final class OrientationModel: ObservableObject {
    @Published var orientation: UIInterfaceOrientation = .unknown
}
```

次は定義したものを宣言

``` SceneDelegate.swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    @ObservedObject(initialValue: OrientationModel()) var model: OrientationModel
```

次に画面回転をハンドリング

``` SceneDelegate.swift
func windowScene(_ windowScene: UIWindowScene, didUpdate previousCoordinateSpace: UICoordinateSpace, interfaceOrientation previousInterfaceOrientation: UIInterfaceOrientation, traitCollection previousTraitCollection: UITraitCollection) {
    model.orientation = windowScene.interfaceOrientation
}
```

次は挙動を確認するためにViewを作成しました。

``` swift
struct CheckOrient: View {
    @EnvironmentObject var model: OrientationModel
    var body: some View {
        Text(model.orientation.isLandscape ? "横" : "縦")
            .font(.title).bold()
    }
}
```

最後にSceneDelegateからmodelを渡すようにします。

``` SceneDelegate.swift
window.rootViewController = UIHostingController(rootView: CheckOrient().environmentObject(model))
```

これで回転したらラベルが切り替わることが確認出来ます。

## 最後の仕上げ

まずは最初に作ったContentViewの処理をOrientationModelに置き換えです。

``` ContentView.swift
struct ContentView: View {
    @EnvironmentObject var model: OrientationModel
    var body: some View {
        Group {
            if model.orientation.isPortrait {
                VStack {
                    Text("Top And Left")
                        .padding(50)
                    Text("Bottom And Right")
                        .padding(50)
                }
            } else {
                HStack {
                    Text("Top And Left")
                        .padding(30)
                    Text("Bottom And Right")
                        .padding(30)
                }
            }
        }
    }
}
```

``` SceneDelegate.swift
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(model))
```

これで画面回転でVStackとHStackの切り替えが完了しました。

アニメーションも加えてみる

``` SceneDelegate.swift
func windowScene(_ windowScene: UIWindowScene, didUpdate previousCoordinateSpace: UICoordinateSpace, interfaceOrientation previousInterfaceOrientation: UIInterfaceOrientation, traitCollection previousTraitCollection: UITraitCollection) {
    withAnimation {
        model.orientation = windowScene.interfaceOrientation
    }
}
```

``` ContentView.swift
var body: some View {
    VStack {  // <= これを最初はGroupにしたけど、そうするとアニメーションを自動でやってくれない
        if model.orientation.isPortrait {
            VStack {
                Text("Top And Left")
                    .padding(50)
                Text("Bottom And Right")
                    .padding(50)
            }
        } else {
            HStack {
                Text("Top And Left")
                    .padding(30)
                Text("Bottom And Right")
                    .padding(30)
            }
        }
    }
}
```

これで回転したときに、フェードアニメーションで中身が切り替わるようになりました！


## その他

Groupだとアニメーションしてくれなくてちょっと焦りました。
なにか他の方法もありそうですね。
もっといい方法あれば教えて下さい！！