SwiftUI 提供簡單直覺的方式來實現動畫,主要有三大核心:
animation
來觸發withAnimation
來觸發當 狀態改變 時,透過 .animation()
自動套用動畫。
struct ImplicitAnimationExample: View {
@State private var isBig = false
var body: some View {
VStack {
Circle()
.fill(Color.blue)
.frame(width: isBig ? 200 : 100, height: isBig ? 200 : 100)
.animation(.easeInOut, value: isBig)
Button("切換大小") {
isBig.toggle()
}
}
}
}
.animation(..., value:)
第一個參數是動畫曲線(速度控制),第二個參數是監聽的狀態值。value
改變時,自動執行動畫使用 withAnimation
包住狀態改變,手動觸發動畫。
struct ExplicitAnimationExample: View {
@State private var isRotated = false
var body: some View {
VStack {
Rectangle()
.fill(Color.green)
.frame(width: 100, height: 100)
.rotationEffect(.degrees(isRotated ? 180 : 0))
Button("旋轉") {
withAnimation(.spring()) {
isRotated.toggle()
}
}
}
}
}
withAnimation
區塊內的狀態改變會有動畫效果SwiftUI 提供多種動畫速度:
.linear
:沒有加速度,從頭到尾速度都一樣。.easeIn
:動畫一開始慢,後面加速。.easeOut
:動畫一開始快,結束時慢下來。.easeInOut
:常見於平滑過渡,進出效果自然。.spring
:有彈性與回彈的感覺,可以調整 阻尼(damping) 與 速度(speed)。範例:比較不同曲線的差異
struct Implicit2AnimationExample: View {
@State private var moveRight = false
var body: some View {
VStack {
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(x: moveRight ? 150 : -150)
.animation(.linear, value: moveRight)
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(x: moveRight ? 150 : -150)
.animation(.easeInOut, value: moveRight)
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(x: moveRight ? 150 : -150)
.animation(.easeOut, value: moveRight)
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(x: moveRight ? 150 : -150)
.animation(.easeIn, value: moveRight)
Circle()
.fill(Color.blue)
.frame(width: 100, height: 100)
.offset(x: moveRight ? 150 : -150)
.animation(.spring, value: moveRight)
Button("切換位置") {
moveRight.toggle()
}
.padding(.top, 50)
}
}
}
當 View 被 新增或移除 時,可以用 .transition()
套用動畫。
struct TransitionExample: View {
@State private var showBox = false
var body: some View {
VStack {
if showBox {
Rectangle()
.fill(Color.red)
.frame(width: 200, height: 200)
.transition(.slide)
}
Button("切換顯示") {
withAnimation(.easeInOut) {
showBox.toggle()
}
}
}
}
}
常用的 Transition:
.opacity
→ 淡入淡出.slide
→ 從邊緣滑入/滑出.scale
→ 放大縮小出現.asymmetric(insertion: .slide, removal: .opacity)
→ 插入和移除使用不同動畫重複動畫(repeatForever)
struct LoopAnimationExample : View {
@State private var isRotated = false
var body: some View {
VStack {
Rectangle()
.fill(Color.green)
.frame(width: 100, height: 100)
.rotationEffect(.degrees(isRotated ? 360 : 0))
.animation(.linear(duration: 2).repeatForever(autoreverses: false), value: isRotated)
}
.onAppear {
isRotated = true
}
}
}
延遲動畫(delay)
struct DelayAnimationExample : View {
@State private var isRotated = true
var body: some View {
VStack {
Rectangle()
.fill(Color.green)
.frame(width: 100, height: 100)
.rotationEffect(.degrees(isRotated ? 360 : 0))
.animation(.easeInOut.delay(1), value: isRotated)
Button("旋轉") {
withAnimation(.spring()) {
isRotated.toggle()
}
}
}
}
}
.animation(..., value:)
綁定狀態變化withAnimation { ... }
包狀態改變.transition(...)
控制新增/移除的效果.repeatForever
、.delay
明天我們將介紹 Canvas,用來在 SwiftUI 中實現更靈活的繪圖與動畫效果。