はじめに
昨日、繰り返しのアニメーションはTimelineView
が相性良さそうっていう記事を書いたんですが、もっと繰り返しアニメーションに適しているものがありました。
それはPhaseAnimator
です。
iOS17から使用できるようになったようです。
PhaseAnimator
を使ってアニメーションを作ってみました。
サンプルアプリ
実装
円の周りに放射状の線を設置するためのView
import SwiftUI
struct RadialLineShape: Shape {
func path(in rect: CGRect) -> Path {
let radius = rect.width / 2
let degreeSeparation : Double = 360.0 / Double(20)
var path = Path()
for index in 0..<Int(360.0/degreeSeparation) {
let degrees = Double(index) * degreeSeparation
let center = CGPoint(x: rect.midX, y: rect.midY)
let innerX = center.x + (radius - rect.size.width * 0.3 / 2) * CGFloat(cos(degrees / 360 * Double.pi * 2))
let innerY = center.y + (radius - rect.size.width * 0.3 / 2) * CGFloat(sin(degrees / 360 * Double.pi * 2))
let outerX = center.x + radius * CGFloat(cos(degrees / 360 * Double.pi * 2))
let outerY = center.y + radius * CGFloat(sin(degrees / 360 * Double.pi * 2))
path.move(to: CGPoint(x: innerX, y: innerY))
path.addLine(to: CGPoint(x: outerX, y: outerY))
}
return path
}
}
import SwiftUI
enum BoltPhase: CaseIterable {
case state1
case state2
}
extension BoltPhase {
var rotationEffect: Angle {
return switch self {
case .state1: .degrees(0)
case .state2: .degrees(-30)
}
}
var backgroundOpacity: CGFloat {
return switch self {
case .state1: 0.8
case .state2: 1.0
}
}
var radialLineShapeOpacity: CGFloat {
return switch self {
case .state1: 1.0
case .state2: 0.0
}
}
}
struct ContentView: View {
var body: some View {
PhaseAnimator(BoltPhase.allCases) { phase in
Image(systemName: "bolt.fill")
.resizable()
.scaledToFit()
.frame(width: 150, height: 150)
.foregroundStyle(.yellow.gradient.shadow(.drop(radius: 3)))
.rotationEffect(phase.rotationEffect)
.padding(15)
.background(.orange.gradient.shadow(.drop(radius: 3)).opacity(phase.backgroundOpacity), in: .circle)
.overlay {
RadialLineShape()
.stroke(.yellow.gradient.opacity(phase.radialLineShapeOpacity), lineWidth: 3.0)
.frame(width: 350, height: 350)
}
}
}
}
おわり
iOS17でアニメーション周りがめっちゃ強化された感じありますね
公式ドキュメント
参考記事