swiftuiswiftui-path

Place views evenly distributed along a path


I would like to place N views, say Circle() for example, around the perimeter of a rounded rectangle. They need to be evenly spaced. I could figure out the math to do this if the main path was a circle by using the circumference and radius of the circle. But there's no such formula for rounded rectangles.

So I'm wondering if there's a way to extract the path from the rounded rect, or really any general shape, and place N evenly spaced views along the path. Even if I have to draw the rectangle path manually, I guess the main question is how to place views along that path.


Solution

  • This can be done by trimming the path and then using currentPoint to find the position along the path. According to the documentation, this returns

    the last point in the path, or nil if the path contains no points

    When you trim from 0 to 0, the currentPoint point is nil. So the loop needs to use a closed range from 1...nDots instead of 0..<nDots:

    private func dottedShape<S: Shape>(shape: S, nDots: Int, dotSize: CGFloat = 10) -> some View {
        GeometryReader { proxy in
            let rect = CGRect(origin: .zero, size: proxy.size)
            let path = shape.path(in: rect)
            ForEach(1...nDots, id: \.self) { n in
                let fraction = Double(n) / Double(nDots)
                if let position = path.trimmedPath(from: 0, to: fraction).currentPoint {
                    Circle()
                        .frame(width: dotSize, height: dotSize)
                        .position(position)
                }
            }
        }
    }
    
    var body: some View {
        dottedShape(
            shape: RoundedRectangle(cornerRadius: 15),
            nDots: 80
        )
        .frame(width: 300, height: 200)
        .foregroundStyle(.blue)
    }
    

    Screenshot