Skip to content

Instantly share code, notes, and snippets.

@mazefest
Created January 21, 2024 04:49
Show Gist options
  • Save mazefest/d29438e2d50c5d8269a7a8b84aa3459c to your computer and use it in GitHub Desktop.
Save mazefest/d29438e2d50c5d8269a7a8b84aa3459c to your computer and use it in GitHub Desktop.
Activity Rings
struct ActivityRingView: View {
@Binding var progress: CGFloat
var mainColor: Color = .red
var lineWidth: CGFloat = 20
var endColor: Color {
mainColor.darker(by: 15.0)
}
var startColor: Color {
mainColor.lighter(by: 15.0)
}
var backgroundColor: Color {
return mainColor.opacity(0.15)
}
var body: some View {
GeometryReader { geo in
ZStack {
Circle()
.stroke(backgroundColor, lineWidth: lineWidth)
Circle()
.trim(from: 0, to: progress)
.stroke(
AngularGradient(
gradient: Gradient(colors: [startColor, endColor]),
center: .center,
startAngle: .degrees(0),
endAngle: .degrees(360)
),
style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)
)
.rotationEffect(.degrees(-90))
Circle()
.frame(width: lineWidth, height: lineWidth)
.foregroundColor(startColor)
.offset(y: -1 * (geo.size.height / 2))
}
.rotationEffect(overlapRotatation())
.frame(idealWidth: 300, idealHeight: 300, alignment: .center)
.animation(.spring(.smooth, blendDuration: 0.5), value: progress)
.rotation3DEffect(
.degrees(180),
axis: (x: 0, y: 1, z: 0)
)
}
}
func overlapRotatation() -> Angle {
let overlapProgress = progress - 1.0
let degrees = overlapProgress * 360.0
return .degrees(-1 * degrees)
}
}
// EXTENSIONS USED
extension Color {
func lighter(by percentage: CGFloat = 30.0) -> Color {
return self.adjust(by: abs(percentage))
}
func darker(by percentage: CGFloat = 30.0) -> Color {
return self.adjust(by: -1 * abs(percentage))
}
func adjust(by percentage: CGFloat = 30.0) -> Color {
var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 1.0
#if canImport(UIKit)
UIColor(self).getRed(&red, green: &green, blue: &blue, alpha: &alpha)
#elseif canImport(AppKit)
NSColor(self).getRed(&red, green: &green, blue: &blue, alpha: &alpha)
#endif
return Color(red: min(red + percentage / 100, 1.0),
green: min(green + percentage / 100, 1.0),
blue: min(blue + percentage / 100, 1.0),
opacity: alpha)
}
}
struct StackedActivityRingViewConfig {
var lineWidth: CGFloat = 15.0
var outterRingColor: Color = .green
var middleRingColor: Color = .blue
var innerRingColor: Color = .red
}
struct StackedActivityRingView: View {
@Binding var outterRingValue: CGFloat
@Binding var middleRingValue: CGFloat
@Binding var innerRingValue: CGFloat
var config: StackedActivityRingViewConfig = .init()
var width: CGFloat = 80.0
var height: CGFloat = 80.0
var body: some View {
GeometryReader { geo in
ZStack {
ActivityRingView(progress: $outterRingValue, mainColor: config.outterRingColor, lineWidth: config.lineWidth)
.frame(width: geo.size.width, height: geo.size.height)
ActivityRingView(progress: $middleRingValue, mainColor: config.middleRingColor, lineWidth: config.lineWidth)
.frame(width: geo.size.width - (2*config.lineWidth), height: geo.size.height - (2*config.lineWidth))
ActivityRingView(progress: $innerRingValue, mainColor: config.innerRingColor, lineWidth: config.lineWidth)
.frame(width: geo.size.width - (4*config.lineWidth), height: geo.size.height - (4*config.lineWidth))
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment