Search code examples
iosswiftswiftuigeometrystroke

Circle with an evenly dotted border in SwiftUI?


I'm trying to create a resizable button that has an evenly dotted circle border around it. If you simply put:

Circle()
  .stroke(color, style: StrokeStyle(lineWidth: 3, lineCap: .butt, dash: [3, radius / 3.82]))
  .frame(width: radius * 2, height: radius * 2)

There might be an uneven distribution of dots as you can see in the below image: enter image description here

Here's a related question with a solution which I tried adapting from UIKit into a SwiftUI struct but also failed.

Can someone please help me either find a way to adapt that "dash" value to create an evenly dashed stroked border with dependence on the radius OR create a custom shape instead?


Solution

  • I have an answer in pure SwiftUI. I think the issue you are running into is simply that you are drawing for part and skipping for part, and you have to take both into account.

    Therefore, you need to come up with the circumference, divide it into the segments of 1 drawn part + 1 undrawn part. The drawn part is simply what looks good to you, so the undrawn part is the segment less the drawn part. You then plug both of those values into the StrokeStyle.dash and you get evenly spaced dots.

    import SwiftUI
    
    struct ContentView: View {
        let radius: CGFloat = 100
        let pi = Double.pi
        let dotCount = 10
        let dotLength: CGFloat = 3
        let spaceLength: CGFloat
    
        init() {
            let circumerence: CGFloat = CGFloat(2.0 * pi) * radius
            spaceLength = circumerence / CGFloat(dotCount) - dotLength
        }
        
        var body: some View {
            Circle()
                .stroke(Color.blue, style: StrokeStyle(lineWidth: 2, lineCap: .butt, lineJoin: .miter, miterLimit: 0, dash: [dotLength, spaceLength], dashPhase: 0))
                .frame(width: radius * 2, height: radius * 2)
        }
    }