Reputation: 1273
I'm having troubles figuring out the proper way to find the x and y coordinates to place and Image
in an overlay
to the correct position for a progress view I'm making. I'd like the end result to look like the screenshot, just not sure what formula I would use. My mathematical skills are subpar to say the least.
GeometryReader { geometry in
ZStack {
Circle()
.trim(from: 0.0, to: 0.5)
.stroke(Color.blue, style: StrokeStyle(lineWidth: 5))
.rotationEffect(.degrees(-180))
.frame(width: geometry.size.width)
.opacity(0.5)
.overlay(Circle()
.trim(from: 0.0, to: CGFloat(configuration.fractionCompleted ?? 0) / 2)
.stroke(Color.blue, style: StrokeStyle(lineWidth: 5))
.rotationEffect(.degrees(-180))
.frame(width: geometry.size.width).overlay(
Circle()
.frame(width: 20, height: 20)
.position(x: 0, y: 0)
}
}
Upvotes: 2
Views: 2586
Reputation: 87774
You only need quite basic geometry here
You need to get C
point coordinates. Following this picture, it will be (x, height / 2 - y)
.
To get y
you need to know that sin α = CD/CA
, as ACD
is a right triangle. From where y = r * sin α
For x
we take a look at the same triangle, but taking cos: cos α = AD/CA
, cos α = (r-x)/r
=> x = r - r * cos α = r * (1 - cos α)
The last thing you need is α
and it's pretty easy: last point of half circle if Pi
, so you just need to multiply it with the progress.
struct ContentView: View {
@State
var progress: CGFloat = 0
var body: some View {
GeometryReader { geometry in
let diameter = geometry.size.width
let radius = diameter / 2
let angle = progress * .pi
ZStack {
Circle()
.trim(from: 0.0, to: 0.5)
.stroke(Color.blue, style: StrokeStyle(lineWidth: 5))
.rotationEffect(.degrees(-180))
.frame(width: diameter)
.opacity(0.5)
.overlay(
Circle()
.trim(from: 0.0, to: progress / 2)
.stroke(Color.blue, style: StrokeStyle(lineWidth: 5))
.rotationEffect(.degrees(-180))
.frame(width: diameter)
.overlay(
Circle()
.frame(width: 20, height: 20)
.position(
x: radius * (1 - cos(angle)),
y: geometry.size.height / 2 - radius * sin(angle)
)
)
)
}
}.onAppear {
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
withAnimation {
progress += 0.04
}
if progress >= 1 {
timer.invalidate()
}
}
}
}
}
Upvotes: 8