在 SwiftUI 中带有均匀虚线边框的圆圈?

Circle with an evenly dotted border in SwiftUI?

我正在尝试创建一个可调整大小的按钮,它周围有一个均匀点缀的圆形边框。如果你简单地输入:

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

如下图所示,圆点分布可能不均匀:

这是一个 related question 的解决方案,我尝试将其从 UIKit 改编为 SwiftUI 结构,但也失败了。

有人可以帮我找到一种方法来调整“破折号”值以创建依赖于半径的均匀虚线描边边框,或者改为创建自定义形状吗?

使用 circle with dash lines uiview

中的部分代码

我得到了适用于 Swiftui 的测试代码:

    import SwiftUI
    
    @main
    struct TestApp: App {
        var body: some Scene {
            WindowGroup {
                ContentView()
            }
        }
    }
    
    struct ContentView: View {

    @State var color = Color.blue
    @State var radius = CGFloat(128)
    @State var painted = CGFloat(6)
    @State var unpainted = CGFloat(6)
    
    let count: CGFloat = 30
    let relativeDashLength: CGFloat = 0.25

    var body: some View {
        Circle()
            .stroke(color, style: StrokeStyle(lineWidth: 3, lineCap: .butt, dash: [painted, unpainted]))
          .frame(width: radius * 2, height: radius * 2)
          .onAppear {
              let dashLength = CGFloat(2 * .pi * radius) / count
              painted = dashLength * relativeDashLength
              unpainted = dashLength * (1 - relativeDashLength)
          }
    }
}

我在纯 SwiftUI 中有一个答案。我认为您 运行 的问题只是您正在绘制部分和跳过部分,您必须将两者都考虑在内。

所以,你需要想出周长,把它分成1个绘制的部分+1个未绘制的部分。绘制的部分只是您看起来不错的部分,因此未绘制的部分是减去绘制的部分的部分。然后,您将这两个值插入 StrokeStyle.dash 并得到均匀间隔的点。

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