1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SwiftUIのAnimationについてふわっと学ぶ(phaseAnimator編)

Last updated at Posted at 2024-10-21

はじめに

今回はWWDC2023の以下動画で見たphaseAnimatorについて実際に触ってキャッチアップしてみました。

普段SwiftUIでAnimationを付与するときは、withAnimationでanimatableなmodifierの値を変更するなどして行うのですが、そういった方法とは少しやり方が違いそうです。

早速みていきましょう。

phaseAnimatorとは

まずは公式のドキュメントを見てみます。

アニメーション内の個別のステップを定義する、提供されたフェーズのコレクションを自動的に循環させることでコンテンツをアニメーション化するコンテナー。

概要
アプリで段階的なアニメーションを作成するには、フェーズ アニメーター ビュー モディファイアのいずれかを使用します。phaseAnimator(_:content:animation:)

nonisolated
func phaseAnimator<Phase>(
   _ phases: some Sequence,
   @ViewBuilder content: @escaping (PlaceholderContentView<Self>, Phase) -> some View,
   animation: @escaping (Phase) -> Animation? = { _ in .default }
) -> some View where Phase : Equatable

phaseAnimatorモディファイアのphasesに設定した配列を自動で循環してアニメーションさせるようです。
と自分で書いていてもイマイチピンとこないので、手を動かします。

無限ループのAnimation

struct MultiStepAnimationView: View {
    
    var body: some View {
        Text("Hello, World!")
            .font(.system(size: 24, weight: .bold))
            .phaseAnimator([1, 2, 3]) { view, phase in
                view
                    .opacity(phase == 2 ? 1 : 0)
                    .scaleEffect(phase * 0.5)
            }
    }
}

phaseAnimatorサンプル.gif

phaseAnimatorモディファイアのcontent引数にて、Animationしたい内容を実装することで、phasesで指定した配列を無限に循環してAnimationが無限ループで再生されるようになりました。

また、以下のように各phase毎にAnimationも指定できました。
そこまで細かくAnimationを指定したいケースは,今のところ遭遇したことがないですが、やろうと思えば相当細かく設定できそうです。

struct MultiStepAnimationView: View {
    
    var body: some View {
        Text("Hello, World!")
            .font(.system(size: 24, weight: .bold))
            .phaseAnimator([1, 2, 3], content: { view, phase in
                view
                    .opacity(phase == 2 ? 1 : 0)
                    .scaleEffect(phase * 0.5)
            }, animation: { phase in
                
                phase == 2 ? .spring : .linear(duration: 1)
            })
    }
}

トリガー付きのAnimation

struct TriggeredMultiStepAnimationView: View {
    
    @State var trigger = 0
    
    var body: some View {
        VStack {
            Spacer()
            Rectangle()
                .frame(width: 100, height: 100)
                .phaseAnimator([1,2,3], trigger: trigger) { view, phase in
                    view
                        .opacity(phase == 2 ? 0 : 1)
                        .rotationEffect(.degrees(phase == 2 ? 360 : 0))
                        .foregroundStyle(phase == 1 ? .red : .blue)
                }
            Spacer()
            Button {
                trigger += 1
            } label: {
                Text("START")
            }
        }
    }
}

トリガー付きPhaseAnimatorサンプル.gif

trigger付きのphaseAnimatorモディファイアを使用した場合、前の例とは違い無限ループのAnimationではなくなります。
triggerに設定したEqutableの値に変更が加えられると、設定したAnimationが発火します。
また、Animationが発火していない状態ではphasesに指定した最初の値で設定したモディファイアの状態が適用されます。
上記の例ですと、通常の状態はphasesが1の状態でopacityrotationEffectforegroundStyleが適用されています。

おわり

思ったより簡単にオリジナルのアニメーションが作れて楽しかったです。
試しにオリジナルのロードアニメーション作ってみました。
こういうAnimationは以前はTimerとか使ってやっていましたが、phaseAnimatorを使うと簡単に実装できます。

ロードアイコン.gif

実装は以下リポジトリに上げました。

1
3
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
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?