👀
SwiftUIのTransitionをカスタマイズする
はじめに
アプリのインターフェース上で新しくTODOを作成したり、TODOが完了したら画面から削除する際に、画面上の要素が追加または削除されます。
Transition
を使って画面が変わるときに適切なアニメーション(トランジション)を設定すること、アプリの中で何か変化が起きたときに、画面がなぜ変わったのか、何が変わったのかがわかりやすくなります。
私たちのインターフェースは、アプリが舞台裏で何をしているかを知るためのポータルであり、トランジションは、起こっている変化を伝える便利な方法です。
トランジションは、新しいビューを表示したり、不要になったビューを削除したりする場合に便利です。
これらは、何が変更されたのか、なぜ変更が発生したのかについてのコンテキストを提供するのに役立ちます。
シンプルなトランジション
public func transition<T>(_ transition: T) -> some View where T : Transition
transition
というViewModifierをViewに対して適用します。
if showAvator {
// スケールが変化しながら表示・削除される
avatorView()
.transition(.scale)
}
if showAvator {
// 透明度が変化しながら表示・削除される
avatorView()
.transition(.opacity)
}
combined(with:)
を組み合わせて複数のTransitionを適用することもできます。
if showAvator {
// スケールと透明度が同時に変化しながら表示・削除される
avatorView()
.transition(.scale.combined(with: .opacity))
}
iOS17からは、新しいTransition
プロトコルに準拠することで、もっと細かく制御できます。
カスタムViewModifierのように、func body(content: Content, phase: TransitionPhase) -> some View {
内でビューを制御できます。
struct Twirl: Transition {
func body(content: Content, phase: TransitionPhase) -> some View {
content
.scaleEffect(phase.isIdentity ? 1 : 0.5)
.opacity(phase.isIdentity ? 1 : 0)
.blur(radius: phase.isIdentity ? 0 : 20)
.rotationEffect(
.degrees(
phase == .willAppear ? 360 :
phase == .didDisappear ? -360 : 0
)
)
.brightness(phase == .willAppear ? 1.0 : 0)
}
}
TransitionPhase
はenum
になっていて、ビューが表示されようとしているか、削除されようとしているのかをswitch
で分岐することができます。
enum TransitionPhase {
case identity
case willAppear
case didDisappear
}
phase.isIdentity: Bool
使用例
struct CustomTransitionDemo: View {
@State private var showAvator = false
var body: some View {
VStack {
Spacer()
if showAvator {
avatorView()
.transition(Twirl())
}
Spacer()
Button {
withAnimation(.spring) {
showAvator.toggle()
}
} label: {
Text("Toggle Avator")
}
}
}
func avatorView() -> some View {
Image( ... ) // アイコン画像など
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.clipShape(.circle)
}
}
参考リンク
Discussion