Created
May 2, 2024 08:47
-
-
Save theoknock/97529d73858af94188612917d81f90de to your computer and use it in GitHub Desktop.
A circular slider that sets the hue
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct ContentView: View { | |
var body: some View { | |
HueControlView() | |
} | |
} | |
struct HueControlView: View { | |
@State var hueAngle: CGFloat = 0.0 | |
@State var angleValue: CGFloat = 0.0 | |
let minimumValue: CGFloat = 0.0 | |
let maximumValue: CGFloat = 360.0 | |
let totalValue: CGFloat = 360.0 | |
private var _knobRadius: CGFloat = 30.0 | |
var knobRadius: CGFloat { | |
get { return _knobRadius } | |
set { _knobRadius = newValue } | |
} | |
private var _diameter: CGSize = CGSize(width: (UIScreen.main.bounds.maxX - 15.0) - (UIScreen.main.bounds.minX + 15.0), height: (UIScreen.main.bounds.maxY - 15.0) - (UIScreen.main.bounds.minY + 15.0)) | |
var diameter: CGSize { | |
get { return _diameter } | |
set { _diameter = newValue } | |
} | |
private var _radius: CGFloat = UIScreen.main.bounds.midX - UIScreen.main.bounds.minX | |
var radius: CGFloat { | |
get { return _radius - (_knobRadius * 1) } | |
set { _radius = newValue } | |
} | |
private let feedbackGenerator = UIImpactFeedbackGenerator(style: .medium) | |
var body: some View { | |
ZStack(alignment: Alignment(horizontal: .center, vertical: .center), content: { | |
Circle() | |
.strokeBorder( | |
AngularGradient(gradient: .init(colors: hueColors()), center: .center), | |
lineWidth: knobRadius | |
) | |
.frame(width: diameter.width, height: diameter.height) | |
.contentShape(Circle()) | |
.rotationEffect(.degrees(-90)) | |
Capsule() | |
.fill(Color(hue: Double(hueAngle) / 360.0, saturation: 1.5, brightness: 1.5)) | |
.frame(width: knobRadius, height: knobRadius * 2) | |
.padding(EdgeInsets(top: 7.5, leading: 15.0, bottom: 7.5, trailing: 15.0)) | |
.offset(y: -radius) | |
.rotationEffect(Angle.degrees(Double(angleValue))) | |
.gesture(DragGesture(minimumDistance: 0.0) | |
.onChanged({ value in | |
change(location: value.location) | |
feedbackGenerator.impactOccurred() | |
})) | |
.shadow(color: Color(hue: Double(hueAngle) / 360.0, saturation: 0.5, brightness: 0.5), radius: 7.5) | |
Text("\(String.init(format: "%.0f", hueAngle))º") | |
.font(.largeTitle).dynamicTypeSize(.xLarge) | |
.foregroundStyle(Color(hue: Double(hueAngle) / 360.0, saturation: 1.0, brightness: 1.0)) | |
}) | |
} | |
func hueColors() -> [Color] { | |
(0 ... 360).map { i in | |
Color(hue: Double(i) / 360.0, saturation: 1.0, brightness: 1.0) | |
} | |
} | |
private func change(location: CGPoint) { | |
// creating vector from location point | |
let vector = CGVector(dx: location.x, dy: location.y) | |
// geting angle in radian need to subtract the knob radius and padding from the dy and dx | |
let angle = atan2(vector.dy - (knobRadius + 15), vector.dx - (knobRadius + 15)) + .pi / 2.0 | |
// convert angle range from (-pi to pi) to (0 to 2pi) | |
let fixedAngle = angle < 0.0 ? angle + 2.0 * .pi : angle | |
// convert angle value to temperature value | |
let value = fixedAngle / (2.0 * .pi) * totalValue | |
if value >= minimumValue && value <= maximumValue { | |
hueAngle = value | |
angleValue = fixedAngle * 180 / .pi // converting to degree | |
} | |
} | |
} | |
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