Equally spaced circular divisions using SwiftUI

Viewed 115

I'm trying to create an equally spaced circular ruler using SwiftUI but there always appears to be one glitch when I use this code:

struct ContentView: View {
    
    let magicNumber: CGFloat = 20
    
    var body: some View {
        VStack {
            GeometryReader { geo in
                Circle()
                    .stroke(Color.red, style: StrokeStyle(lineWidth: geo.size.width/5, lineCap: .butt, dash: [1, magicNumber]))
            .padding(geo.size.width/10)
                }
        }.padding()
    }
}

As can be seen here...

enter image description here

I've separated out the "magicNumber" variable in the hope there is a function/equation I could use to determine what it should be for the spacing to always be equal. Any tips? Or perhaps there is a better way to achieve this?

1 Answers

Rather than trying to dynamically calculate a magic number, maybe just better to define a shape and tell it how many sections you want. That way you can be guaranteed that they'll be evenly spaced.

struct ContentView : View {
    var body: some View {
        MyShape(sections: 20, lineLengthPercentage: 0.3)
            .stroke(Color.red)
    }
}

struct MyShape : Shape {
    var sections : Int
    var lineLengthPercentage: CGFloat
    
    func path(in rect: CGRect) -> Path {
        let radius = rect.width / 2
        let degreeSeparation : Double = 360.0 / Double(sections)
        var path = Path()
        for index in 0..<Int(360.0/degreeSeparation) {
            let degrees = Double(index) * degreeSeparation
            let center = CGPoint(x: rect.midX, y: rect.midY)
            let innerX = center.x + (radius - rect.size.width * lineLengthPercentage / 2) * CGFloat(cos(degrees / 360 * Double.pi * 2))
            let innerY = center.y + (radius - rect.size.width * lineLengthPercentage / 2) * CGFloat(sin(degrees / 360 * Double.pi * 2))
            let outerX = center.x + radius * CGFloat(cos(degrees / 360 * Double.pi * 2))
            let outerY = center.y + radius * CGFloat(sin(degrees / 360 * Double.pi * 2))
            path.move(to: CGPoint(x: innerX, y: innerY))
            path.addLine(to: CGPoint(x: outerX, y: outerY))
        }
        return path
    }
}

Related