cole
cole

Reputation: 1273

SwiftUI circle position x and y

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)
    }
}

enter image description here

Upvotes: 2

Views: 2586

Answers (1)

Phil Dukhov
Phil Dukhov

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

Related Questions