5
1

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 1 year has passed since last update.

お題は不問!Qiita Engineer Festa 2023で記事投稿!

【SwiftUI】Pinterestの起動アニメーションを再現する

Last updated at Posted at 2023-07-20

はじめに

Pinterestを起動すると、最初にロゴが楽しげに動くアニメーションが表示されます。
今回は、このアニメーションを再現します。

デモ

型を定義

まず、Viewがある状態に変化するまでにかかる時間、ある状態でのサイズと位置をまとめたAnimationStepを定義します。

struct AnimationStep {
    let duration: Double // アニメーションの継続時間
    let diameter: CGFloat // ロゴの直径
    let yOffset: CGFloat // y軸方向のオフセット
    
    init(
        duration: Double,
        diameter: CGFloat = 100,
        yOffset: CGFloat = 0
    ) {
        self.duration = duration
        self.diameter = diameter
        self.yOffset = yOffset
    }
}

アニメーションを配列で定義

次に、全体のアニメーションを配列で定義します。
animationStepsプロパティに注目して下さい。
最初に少し小さくなって、大きくなって、元のサイズに戻るアニメーションがあり、
その後少し下に移動して、今度は上に消えていくアニメーションがあります。

import SwiftUI

struct ContentView: View {
    @State private var diameter: CGFloat = 100
    @State private var yOffset: CGFloat = 0
    
    private let animationSteps: [AnimationStep] = [
        AnimationStep(duration: 0.1, diameter: 80),
        AnimationStep(duration: 0.2, diameter: 120),
        AnimationStep(duration: 0.1, diameter: 100),
        AnimationStep(duration: 0.3, yOffset: 150),
        AnimationStep(duration: 0.6, yOffset: -600),
    ]
    
    var body: some View {
        Image(systemName: "pencil.circle.fill")
            .resizable()
            .foregroundStyle(.red)
            .frame(width: diameter, height: diameter)
            .offset(y: yOffset)
    }
}

アニメーションを再生する

最後に、作ったアニメーションを再生します。

onAppear内の処理に注目して下さい。

animationSteps.enumerated()で取得したanimationStepsのインデックスと要素をfor文で回しています。

withAnimationで指定しているアニメーションは、stepduration分継続し、それまでのstepdurationの合計分遅延させています。

こうすることにより、stepのアニメーションが終わってから次のstepのアニメーションが始まるようになります。

struct ContentView: View {
    @State private var diameter: CGFloat = 100
    @State private var yOffset: CGFloat = 0
    
    private let animationSteps: [AnimationStep] = [
        AnimationStep(duration: 0.1, diameter: 80),
        AnimationStep(duration: 0.2, diameter: 120),
        AnimationStep(duration: 0.1, diameter: 100),
        AnimationStep(duration: 0.3, yOffset: 150),
        AnimationStep(duration: 0.6, yOffset: -600),
    ]
    
    var body: some View {
        Image(systemName: "pencil.circle.fill")
            .resizable()
            .foregroundStyle(.red)
            .frame(width: diameter, height: diameter)
            .offset(y: yOffset)
            .onAppear {
                for (index, step) in animationSteps.enumerated() {
                    withAnimation(
                        .easeInOut(duration: step.duration)
                        .delay(index > 0 ? animationSteps[0..<index].reduce(0) { $0 + $1.duration } : 0)
                    ) {
                        diameter = step.diameter
                        yOffset = step.yOffset
                    }
                }
            }
    }
}

おわりに

今回は、理解しやすいプログラムと複雑なアニメーションを両立させることができました。この記事が参考になったという方は、いいねとフォローよろしくお願いします☺️

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?