はじめに
SwiftUIでAnimationさせるときにtransitionというModifierを見かけたのですが、これが何をやっているのかよくわかっていなかったので、ふわっと学びたいと思います。
環境
- Xcode16
- iOS17
transitionとは
公式のドキュメントによると、Viewの表示、非表示切り替えの際のAnimationを制御するModifireのようです。
このビューが表示されたり消えたりすると、トランジションが適用され、アニメーション化が可能になります。
次のコードは、条件に応じて MyView を表示し、表示または非表示になったときにスライド遷移を使用して表示します。if isActive { MyView() .transition(.slide) } Button("Toggle") { withAnimation { isActive.toggle() } }
transition
ModifierにAnyTransition
を指定することで、表示、非表示時のAnimationの指定をしています。
上記ではtransition
Modifierにslide
というAnyTransitionのstaticプロパティを指定しているので、スライドのAnimationになりますが他にもデフォルトで指定できるAnyTransitionのstaticプロパティがありました。
scale
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
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を実装してみました。
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の表示、非表示時ですがどうなると表示・非表示にあたるのか気になったので以下ケースで実験してみました。
-
if
制御で表示・非表示切り替えしたとき -
opacity
制御で表示・非表示切り替えしたとき
1のケースについては、上述しているケースですので当然Transitionでいうところの表示・非表示にあたります。
2のケースにていては、transitionで指定したAnimationは効きませんでした。
理由としてはおそらくopacityはあくまで透明度が0になり見えなくなっているだけで要素としては存在しているため、SwiftUIとしては表示状態だからでしょう。
よって、上記ケースではTransitionが効くのは1のケースだけでした。
おわり
transitionについて、キャッチアップ前と比べると理解度が増したように思います。
ぬるぬる動くおしゃれなアプリには必須のテクニックだと思うので、ガンガン使っていきたいです。