Thanks for feedback.
Now, you may want to have also hours hand ?
Here is a simple example on how to do it (should be improved, notably for aligning the 2 hands).
struct ContentView: View {
@State private var angleMinutes : Double = 0
@State private var angleHour : Double = 0
@State private var cumulAngleMinutes : Double = 0
@State private var currentVector = CGVector.zero
@State private var lastVector = CGVector.zero // Keep track where we ended
@State private var handColor = UIColor(red: 0, green: 0.866, blue: 0.866, alpha: 1) // UIColor.green
let minutesHandHeight = CGFloat(100) // In case value changed later
let hourHandHeight = CGFloat(80) // Smaller and larger than minutes
var color: UIColor {
let red = abs(sin(angleMinutes * .pi / 360))
let green = abs(sin((angleMinutes + 240) * .pi / 360))
let blue = abs(sin((angleMinutes + 120) * .pi / 360))
return UIColor(red: red, green: green, blue: blue, alpha: 1)
}
var body: some View {
Rectangle() // Hours
.fill(Color.blue)
.frame(width: 10, height: hourHandHeight)
.rotationEffect(Angle(degrees: angleHour), anchor: .bottom)
.offset(x: 0, y: hourHandHeight + 30) // 30: defaultspacing
Rectangle() // Minutes
.fill(Color(handColor))
.frame(width: 8, height: minutesHandHeight)
.rotationEffect(Angle(degrees: angleMinutes), anchor: .bottom)
.gesture(
DragGesture()
.onChanged { value in
currentVector = CGVector(dx: value.translation.width + lastVector.dx, dy: value.translation.height + lastVector.dy)
let radians = atan2(currentVector.dy - minutesHandHeight, currentVector.dx)
let newAngle = 90 + radians * 180 / .pi // That is the new rotation angle for the hand
var deltaAngle = Double(newAngle) - self.angleMinutes // What is the increment ; it may jump 360°
if deltaAngle > 180 { deltaAngle -= 360 }
if deltaAngle < -180 { deltaAngle += 360 }
cumulAngleMinutes += deltaAngle
self.angleMinutes = Double(newAngle)
self.angleHour = self.cumulAngleMinutes / 12 // hourHand rotates 1/12 of minutes
handColor = color
}
.onEnded { _ in // Needed to start a new Gesture
self.lastVector = currentVector
}
)
}
}
Some explanations:
- Only the minutes hand has a gesture. Hours angle is computed from minutes'
- The hours rotate at 1/12 of minutes, so angleHour is computed accordingly.
- But, minutes angle return to 0 each turn ; and hours must increment. So, you need to keep the cumulated rotation of minutes and compute hours angle from this cumulated value.
- in addition, when minutes pass 45, angle jumps from 270 to -90. Hence the test on deltaAngle
- for the fun, the minutes hand changes colour as it rotates…
Hope you'll find it useful as a starting point.