I would like to implement circular dragging in SwiftUI, but am not sure of the best way to go about it.
Here is the basic dragging code - there is one small draggable circle which I would like to limit to the bounds of the bigger one during the updating
phase of the DragGesture. At the moment the black circle is draggable across the entire view.
import SwiftUI
struct ContentView: View {
@State private var position = CGSize.zero
@GestureState var dragOffset: CGSize = .zero
private var dragRadius: CGFloat = 200.0
var body: some View {
ZStack {
Circle()
.fill(Color.red)
.frame(width: dragRadius, height: dragRadius)
Circle()
.fill(Color.black)
.frame(width: dragRadius / 4, height: dragRadius / 4)
.offset(x: position.width + dragOffset.width, y: position.height + dragOffset.height)
.gesture(
DragGesture()
.updating($dragOffset, body: { (value, state, transaction) in
// Need to clamp to circular bounds here??
state = value.translation
})
.onEnded({ (value) in
self.position.height += value.translation.height
self.position.width += value.translation.width
})
)
}
}
}
I wonder whether it's a case of using trigonometry and polar co-ordinates to calculate the distance from the centre and limit to the radius in the direction of the dragged circle, or is there an easier way to get SwiftUI to "see" the circular bounds of a view?
its not so much code to implement that. I just calculate distance between points (made an extension for that like in that question) and use that coefficient to make actual distance shorter. Is that what you want to reach?
import SwiftUI
extension CGPoint {
func distance(to point: CGPoint) -> CGFloat {
return sqrt(pow((point.x - x), 2) + pow((point.y - y), 2))
}
}
struct ContentView: View {
@State private var position = CGPoint(x: 100, y: 100)
private var dragDiametr: CGFloat = 200.0
var body: some View {
return
VStack{
Text("current position = (x: \(Int(position.x)), y: \(Int(position.y)))")
Circle()
.fill(Color.red)
.frame(width: dragDiametr, height: dragDiametr)
.overlay(
Circle()
.fill(Color.black)
.frame(width: dragDiametr / 4, height: dragDiametr / 4)
.position(x: position.x, y: position.y)
.gesture(DragGesture()
.onChanged(){value in
let currentLocation = value.location
let center = CGPoint(x: self.dragDiametr/2, y: self.dragDiametr/2)
let distance = center.distance(to:currentLocation)
if distance > self.dragDiametr / 2 {
let k = (self.dragDiametr / 2) / distance
let newLocationX = (currentLocation.x - center.x) * k+center.x
let newLocationY = (currentLocation.y - center.y) * k+center.y
self.position = CGPoint(x: newLocationX, y: newLocationY)
}else{
self.position = value.location
}
})
)
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With