Skip to content

Instantly share code, notes, and snippets.

@Koshimizu-Takehito
Last active September 9, 2024 05:17
Show Gist options
  • Save Koshimizu-Takehito/09e9fea1325b1c5ef01d53b2fb532c78 to your computer and use it in GitHub Desktop.
Save Koshimizu-Takehito/09e9fea1325b1c5ef01d53b2fb532c78 to your computer and use it in GitHub Desktop.
RingSlider
import SwiftUI
struct ContentView: View {
@State var ratio: Double = 0
var body: some View {
ZStack {
Color(hue: 1, saturation: 0.3, brightness: 1)
.hueRotation(.degrees(360 * ratio))
.ignoresSafeArea()
RingSlider(ratio: $ratio)
.frame(width: 300)
Button("Reset") { reset() }
.font(.title)
.fontWeight(.bold)
}
.tint(.blue)
}
private func reset() {
withAnimation {
ratio = ratio.rounded(.toNearestOrEven)
}
}
}
struct RingSlider: View, Animatable {
@Binding var ratio: Double
var animatableData: Double
init(ratio: Binding<Double>) {
_ratio = ratio
animatableData = ratio.wrappedValue
}
var body: some View {
GeometryReader { geometry in
let size = geometry.size
let ringRadius = min(size.width, size.height) / 2
ZStack {
// Ring
let dotSize = ringRadius / 3
let lineWidth = 0.5 * dotSize
Circle()
.strokeBorder(lineWidth: lineWidth)
.foregroundStyle(.tint)
.frame(width: ringRadius * 2, height: ringRadius * 2)
// Dot
let r = ringRadius - lineWidth / 2
let t = 2 * .pi * animatableData - .pi / 2
Circle()
.frame(width: dotSize, height: dotSize)
.foregroundStyle(.white)
.shadow(radius: 2)
.offset(x: r * cos(t), y: r * sin(t))
}
.gesture(
DragGesture().onChanged { value in
var location = value.location
location.x -= ringRadius
location.y -= ringRadius
location.y *= -1
let t = atan2(location.x, location.y) / (2 * .pi)
ratio = (t + 1).remainder(dividingBy: 1)
}
)
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
#Preview {
ContentView()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment