【SwiftUI】Heroアニメーションを試す

# 1.はじめに
先日HeroアニメーションというUIを初めて知りました。

Heroアニメーションとは画面遷移時にコンテンツの比率を変えながら遷移して
シームレスに移動したように見せるアニメーションのことです。

具体的にはiPhoneの写真なんかもそうですよね。
写真を選択するとズームされて全画面に表示されるやつです。

今回はそんな最近当たり前となってきたHeroアニメーションを
SwiftUIの基本ライブラリ使って実装してみたいと思います。


# 2.イメージ
今回の記事の内容を使うと下記のようなUIが作れるようになります。

![001.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233342/7873b206-3688-a648-44c7-b14ca3c3fa25.gif)

# 3.実装
それでは実装していきましょう。

今回は```matchedGeometryEffect```というモディファイアを使用します。

## 3.1.まずはベースを作る

ベースとなる画面を作っていきます。

```swift
struct ContentsView: View {
    @State var show = false
    
    var body: some View {
        VStack {
            if !self.show {
                Image("iPhone-5c-mockup-blue")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(maxWidth: 200, maxHeight: 200)
            } else {
                Image("iPhone-5c-mockup-blue")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .background(Color(red: 66/255, green: 178/255, blue: 236/255))
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
            }
        }
        .onTapGesture {
            self.show.toggle()
        }
    }
}
```

Imageを１つ表示するだけのViewとなっており、
状態変数showの状態によってImageのサイズ、背景色を変えています。

そうすることで下記のようなタップすると写真が画面いっぱいに広がるようになります。

![002.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233342/47290e14-cc3e-c114-b244-042b97e25d17.gif)

この状態では画像の切り替えが一瞬で行われるので目がチカチカしてしまいます。


## 3.2.Heroアニメーションを適用する

いよいよHeroアニメーションを適用していきます。
先ほどのコードに少々手を加えます。

```swift:ContentView.swift

struct ContentView: View {
    // 画像がタップされているかどうかを保持する状態変数
    @State var show = false
    @Namespace var namespace
    
    var body: some View {
        VStack {
            if !self.show {
                // タップされていない時に表示する画像
                Image("iPhone-5c-mockup-blue")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(maxWidth: 200, maxHeight: 200)
                    .matchedGeometryEffect(id: "zoom", in: namespace)
                
            } else {
                // タップされた時に表示する画像（高さと横幅をinfinityにしている）
                Image("iPhone-5c-mockup-blue")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .background(Color(red: 66/255, green: 178/255, blue: 236/255))
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .matchedGeometryEffect(id: "zoom", in: namespace)
            }
        }
        .onTapGesture {
            withAnimation(.default) {
                self.show.toggle()
            }

        }
    }
}
```

### アニメーションさせたいViewにmatcheGeometryEffectを設定する

Namespaceとidを渡してアニメーションさせるViewをグルーピングします。
idはグルーピングしたいViewで同じであればOKです。

```swift
.matchedGeometryEffect(id: "zoom", in: namespace)
```

### アニメーションを設定する

状態変数```show```の更新を```withAnimation```のクロージャに渡してあげます。
こうすることによってshow変数の更新に関わるViewの再描画にアニメーションが付与されます。

```swift
withAnimation(.default) {
       self.show.toggle()
}
```

そしてできたものがこちらです。
アニメーションが付与されました！

![003.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233342/15781b1d-fe88-941e-3e4b-a3c4ba634beb.gif)


# 4.おわりに
いかがでしたでしょうか。
思っていたより簡単に実装することができましたね。

あとは倍率を変えたり他のViewと組み合わせることで最初にご覧いただいたようなUIも作ることができます。

みなさんぜひお試しください!