3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

1.はじめに

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

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

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

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

2.イメージ

今回の記事の内容を使うと下記のようなUIが作れるようになります。

001.gif

3.実装

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

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

3.1.まずはベースを作る

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

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を1つ表示するだけのViewとなっており、
状態変数showの状態によってImageのサイズ、背景色を変えています。

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

002.gif

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

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

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

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です。

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

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

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

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

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

003.gif

4.おわりに

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

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

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

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?