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

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

Last updated at Posted at 2024-10-03

はじめに

SwiftUIでAnimationさせるときにtransitionというModifierを見かけたのですが、これが何をやっているのかよくわかっていなかったので、ふわっと学びたいと思います。

環境

  • Xcode16
  • iOS17

transitionとは

公式のドキュメントによると、Viewの表示、非表示切り替えの際のAnimationを制御するModifireのようです。

このビューが表示されたり消えたりすると、トランジションが適用され、アニメーション化が可能になります。
次のコードは、条件に応じて MyView を表示し、表示または非表示になったときにスライド遷移を使用して表示します。

if isActive {
   MyView()
       .transition(.slide)
}
Button("Toggle") {
   withAnimation {
       isActive.toggle()
   }
}

transitionModifierにAnyTransitionを指定することで、表示、非表示時のAnimationの指定をしています。

上記ではtransitionModifierにslideというAnyTransitionのstaticプロパティを指定しているので、スライドのAnimationになりますが他にもデフォルトで指定できるAnyTransitionのstaticプロパティがありました。

scale

scaleTransitionSample.gif

struct ScaleTransitionView: View {
    
    @State var isShown = true
    
    var body: some View {
        VStack {
            Spacer()
            if isShown {
                Circle()
                    .frame(width: 100)
                    .transition(.scale(scale: 0))
            }
            Spacer()
            Button {
                withAnimation(.linear(duration: 1.0)) {
                    isShown.toggle()
                }
            } label: {
                Text(isShown ? "Hide" : "Show")
            }
            Spacer()
        }
    }
}

blurReplace

blurTransitionSample.gif

struct BlurReplaceTransitionView: View {
    
    @State var isShown = true
    
    var body: some View {
        VStack {
            Spacer()
            if isShown {
                Circle()
                    .frame(width: 100)
                    .transition(.blurReplace)
            }
            Spacer()
            Button {
                withAnimation(.linear(duration: 1.0)) {
                    isShown.toggle()
                }
            } label: {
                Text(isShown ? "Hide" : "Show")
            }
            Spacer()
        }
    }
}

その他にもいろいろありました。

AnyTransition 内容
opacity 徐々に透明度が変わるAnimation
move(Edge) 引数で指定した方向に移動するAnimation(slideみたいにふわっと消えない)
offset(x, y) 引数で指定したoffset分移動するAnimation(slideみたいにふわっと消えない)
push(Edge) 引数で指定した方向から移動するAnimation(slideみたいにふわっと消える
identity Animationしない
asymmetric(insertion, removal) 表示時と非表示時のAnimationの内容をそれぞれ引数でAnyTransitionとして指定できる

上記のtransitionのサンプルは以下GitHubリポジトリにありますのでよかったら見てみてください。

カスタムTransition

上記で取り上げたTransitionはAnyTransitionにデフォルトで備わっているものでしたが、自身でオリジナルのTransitionを実装することも可能なようです。

試しに以下のようなTransitionを実装してみました。

CustomTransitionSample.gif

struct CustomTransitionView: View {
    
    @State var isShown = true
    
    var body: some View {
        VStack {
            Spacer()
            if isShown {
                Circle()
                    .frame(width: 100)
                    // 回転と透明度のtransitionを併用
                    .transition(.rotation.combined(with: .opacity))
            }
            Spacer()
            Button {
                withAnimation(.linear(duration: 1.5)) {
                    isShown.toggle()
                }
            } label: {
                Text(isShown ? "Hide" : "Show")
            }
            Spacer()
        }
    }
}

// 回転効果を付与するModifierを定義
struct RotationModifier: ViewModifier {
    
    let degrees: CGFloat
    let axis: (x: CGFloat, y: CGFloat, z: CGFloat)
    
    init(degrees: CGFloat,
         axis: (x: CGFloat, y: CGFloat, z: CGFloat) = (0, 0, 0)) {
        self.degrees = degrees
        self.axis = axis
    }
    
    func body(content: Content) -> some View {
        content
            .rotation3DEffect(.degrees(degrees), axis: axis)
    }
}

extension AnyTransition {

    // 回転するTransitionを定義
    static var rotation: AnyTransition {
        .modifier(active: RotationModifier(degrees: 720, axis: (1, 5, 5)),
                  identity: RotationModifier(degrees: 0))
    }
}

AnyTransitionにstatic varとしてAnyTransitionを返すプロパティを実装することで、オリジナルのTransitionを定義できます。
その中で行なっている.modifierでは表示時、非表示時にどのような効果を付与するかをViewModifierで設定できます。

今回は回転効果を付与するRotationModifierというViewModifierを実装しそれを指定しました。

またAnyTransitionにはcombineというメソッドがあり、これにより複数のTransitionを合体させることもできます。
上記の例ではオリジナルのrotationに加えてopacityを合体させてふわっと消えるようにしました。

表示、非表示の定義

冒頭でもあったように、Transitionで制御できるAnimationは対象のViewの表示、非表示時ですがどうなると表示・非表示にあたるのか気になったので以下ケースで実験してみました。

  1. if制御で表示・非表示切り替えしたとき
  2. opacity制御で表示・非表示切り替えしたとき

1のケースについては、上述しているケースですので当然Transitionでいうところの表示・非表示にあたります。
2のケースにていては、transitionで指定したAnimationは効きませんでした。

理由としてはおそらくopacityはあくまで透明度が0になり見えなくなっているだけで要素としては存在しているため、SwiftUIとしては表示状態だからでしょう。
よって、上記ケースではTransitionが効くのは1のケースだけでした。

おわり

transitionについて、キャッチアップ前と比べると理解度が増したように思います。
ぬるぬる動くおしゃれなアプリには必須のテクニックだと思うので、ガンガン使っていきたいです。

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