I have a view that I want to be able to drag around - kind of like a non-free spinning wheel of fortune. The dragging works fine on the y axis and at the bottom of the wheel, but with the x - the view rotates in the wrong direction when dragging the top of the wheel.
I am pretty sure this just requires a little extra logic - but everything I have tried doesn't work - such as checking to see if v.startLocation.x < v.location.x
.
import SwiftUI
struct SpinningSelectorWheel: View {
var numberOfSegments: Int = 5
private var colorArray:[Color] = [.red, .green, .blue, .yellow, .brown, .gray, .purple, .red, .green, .blue, .yellow, .brown, .gray, .purple]
@State private var angle: CGFloat = 0
@State private var lastAngle: CGFloat = 0
@State private var length : CGFloat = 400
@State private var gtheta: CGFloat = 0
@State private var gstartx: CGFloat = 0
@State private var gstarty: CGFloat = 0
var body: some View {
GeometryReader
{ fullsize in
//Text("\(angle)")
VStack
{
Text("\(gtheta)")
Text("\(gstartx)")
Text("\(gstarty)")
Text("\(length)")
}
ForEach((1...numberOfSegments).reversed(), id: \.self)
{segment in
Path { path in
path.move(to: CGPoint(x: fullsize.size.width/2, y: fullsize.size.height/2))
path.addArc(center: .init(x: fullsize.size.width/2, y: fullsize.size.height/2), radius: 150, startAngle: Angle(degrees: Double(1-segment) * Double(360/numberOfSegments)), endAngle: Angle(degrees: Double(360/numberOfSegments)), clockwise: false)
}
.fill(colorArray[segment-1])
}
.rotationEffect(.degrees(Double(self.angle))).gesture(DragGesture().onChanged
{ v in
var theta = (atan2(v.location.x - self.length / 2, self.length / 2 - v.location.y) - atan2(v.startLocation.x - self.length / 2, self.length / 2 - v.startLocation.y)) * 180 / .pi
gtheta = theta
gstartx = v.startLocation.x
gstarty = v.startLocation.y
if (theta < 0) { theta += 360}
self.angle = theta + self.lastAngle
}
.onEnded { v in
self.lastAngle = self.angle
}
)
}
}
}
Something I wrote for UIKit that may be adapted to SwiftUI :
// rotation from point lPrevPoint to lTouchPoint
let lDeltaDirectionX = Double((lTouchPoint.x - lPrevPoint.x) / 2.0)
let lDeltaDirectionY = Double((lTouchPoint.y - lPrevPoint.y) / 2.0)
// only do 1 direction at a time ( the one with biggest change)
// then you check in which part of the screen you are :
if abs(lDeltaDirectionX) > abs(lDeltaDirectionY) {
if lPrevPoint.y > self.frame.size.height / 2.0 {
orientation = fmod(orientation-lDeltaDirectionX + 360.0, 360.0)
} else {
orientation = fmod(orientation+lDeltaDirectionX + 360.0, 360.0)
}
}
else
{
if lPrevPoint.x > self.frame.size.width / 2.0 {
orientation = fmod(orientation+lDeltaDirectionY + 360.0, 360.0)
} else {
orientation = fmod(orientation-lDeltaDirectionY + 360.0, 360.0)
}
}
orientation is the angle of rotation . Not perfect because it does not take into account the distance from view center to have a more accurate value. But can be a good start.