How to place lines at the edge of the circle in SwiftUI?

102 views Asked by At

I have a circle and I want to place small rectanlges at the edge of the circle. I want rectangles to placed on edge by only defining the angle. i.e if angle is 90, rectangle should be on top center of the circle. If say 120, it should be placed a little ahead of 90 degree's rectangle along with a little tilt, so rectangle and circle should be in sync. I have tried alot to solve this, but I am unable to crack this yet.

Below is the code that I've written so far. Its basically a starter code, nothing more.

struct ContentView: View {
    var body: some View {
        GeometryReader { geo in
            VStack {
                ZStack {
                    Circle()
                        .stroke(lineWidth: 10)
                        .frame(width: 220)
                    Rectangle()
                        .frame(width: 15, height: 50)
                        .position(x: 0, y: 0)
                } //ZStack
                .frame(width: 220, height: 220, alignment: .center)
                .background(Color.green)
            } //VStack
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.blue)
        }
    }
}

What I want it to look like

Any help would be appreciated

1

There are 1 answers

0
Benzy Neez On BEST ANSWER

I would suggest applying .offset followed by .rotationEffect to the rectangles.

Like this:

struct ContentView: View {

    let radius: CGFloat = 110
    let tickMarkLength: CGFloat = 50

    private func tickMark(degrees: CGFloat) -> some View {
        Rectangle()
            .frame(width: 15, height: tickMarkLength)
            .offset(y: -radius - (tickMarkLength / 2))
            .rotationEffect(.degrees(degrees - 90))
    }

    var body: some View {
        ZStack {
            Circle()
                .stroke(lineWidth: 10)
                .frame(width: radius * 2, height: radius * 2)
                .background(
                    Circle().fill(.green)
                )
            tickMark(degrees: 90)
            tickMark(degrees: 120)
        }
        .frame(
            width: (radius * 2) + (tickMarkLength * 2),
            height: (radius * 2) + (tickMarkLength * 2)
        )
        .background(.blue)
    }
}

Screenshot

In the code you provided in the question, you had a GeometryReader, although it didn't seem to be serving any purpose. However, if you were intending to derive the radius of the circle from the size of the frame it is contained in, then this is possible too. A few small adaptions are needed:

struct ContentView: View {
    let tickMarkLength: CGFloat = 50

    private func tickMark(radius: CGFloat, degrees: CGFloat) -> some View {
        // implementation as before
    }

    var body: some View {
        GeometryReader { geo in
            let radius = (geo.size.height / 2) - tickMarkLength
            ZStack {
                Circle()
                    // modifiers as before
                tickMark(radius: radius, degrees: 90)
                tickMark(radius: radius, degrees: 120)
            }
            .frame(width: geo.size.width, height: geo.size.height)
        }
        .frame(width: 320, height: 320) // just an example
        .background(.blue)
    }
}