Skip to content

Instantly share code, notes, and snippets.

@Koshimizu-Takehito
Created July 18, 2023 23:40
Show Gist options
  • Save Koshimizu-Takehito/93c5891e89d65eadf2164351ca1f2d76 to your computer and use it in GitHub Desktop.
Save Koshimizu-Takehito/93c5891e89d65eadf2164351ca1f2d76 to your computer and use it in GitHub Desktop.
おしゃれなローディングアニメーション
import SwiftUI
// Thanks to @Ren_yello
// https://twitter.com/ren_yello/status/1681135824145113089?s=61&t=Z69bUaia8ogjDHp-N7Xvvg
struct ContentView: View {
@State var ratio: Double = 0
@State var angle: CGFloat = 0
@State var delta: CGFloat = 0
var body: some View {
ZStack {
PercentText(ratio: ratio)
LoadingCircles(angle: angle, delta: delta)
.frame(width: 180, height: 180)
}
.background(Color.white.opacity(0.0001))
.onTapGesture {
ratio = 0
angle = 0
delta = .pi / 2
withAnimation(.linear(duration: 6.0).repeatForever(autoreverses: false)) {
angle = 6.0 * .pi
}
withAnimation(.easeOut(duration: 12.0)) {
ratio = 1
}
Task {
try await Task.sleep(nanoseconds: 12 * 1_000_000_000)
withAnimation(.easeOut(duration: 1.0)) {
angle = 0
delta = 2 * .pi
}
}
}
}
}
struct PercentText: View, Animatable {
var ratio: Double
var animatableData: Double {
get { ratio }
set { ratio = newValue }
}
var body: some View {
let value = Int(min(max(ratio, 0), 1) * 100)
Text("\(value)%")
.font(.title)
.fontWeight(.semibold)
.monospacedDigit()
}
}
struct LoadingCircles: View {
var angle: CGFloat
var delta: CGFloat
var angle1: CGFloat { angle * 6.0 / 6.0 }
var angle2: CGFloat { angle * 8.0 / 6.0 }
var angle3: CGFloat { angle * 12.0 / 6.0 }
var body: some View {
ZStack {
PartialCircle(start: angle1, end: angle1 + delta)
.stroke(lineWidth: 3.0)
.foregroundStyle(Color.red)
PartialCircle(start: angle2, end: angle2 + delta)
.stroke(lineWidth: 3.0)
.padding(16)
.foregroundStyle(Color.blue)
PartialCircle(start: angle3, end: angle3 + delta)
.stroke(lineWidth: 3.0)
.padding(32)
.foregroundStyle(Color.yellow)
}
}
}
struct PartialCircle: Shape, Animatable {
var start: Double
var end: Double
var animatableData: AnimatablePair<Double, Double> {
get { AnimatablePair(start, end) }
set { (start, end) = (newValue.first, newValue.second) }
}
func path(in rect: CGRect) -> Path {
let radius: CGFloat = min(rect.width, rect.height)/2
let center = CGPoint(x: rect.midX, y: rect.midY)
let start = Angle(radians: start)
let end = Angle(radians: end)
var path = Path()
path.addArc(center: center, radius: radius, startAngle: start, endAngle: end, clockwise: false)
return path
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment