iosswiftuibezierpathuigraphicscontext

Swift draw alternating dashed and dotted line with UIBezierPath


I'm attempting to draw a line with a UIBezierPath that alternates between dots and dashes. Here's my code so far:

 func drawAlternatingDashesAndDots() {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 10,y: 10))
        path.addLine(to: CGPoint(x: 290,y: 10))
        path.lineWidth = 8

        let dots: [CGFloat] = [0.001, path.lineWidth * 2]
        path.setLineDash(dots, count: dots.count, phase: 0)

        for i in 0...10 {
            if i % 2 == 0 {
              // Even Number
                path.lineCapStyle = CGLineCap.round

            } else {
              // Odd Number
                path.lineCapStyle = CGLineCap.square
            }
        }
        UIGraphicsBeginImageContextWithOptions(CGSize(width:300, height:20), false, 2)
        UIColor.white.setFill()
        UIGraphicsGetCurrentContext()!.fill(.infinite)
        UIColor.black.setStroke()
        path.stroke()
        
        let image = UIGraphicsGetImageFromCurrentImageContext()
        dashDotDashLineView.image = image
        UIGraphicsEndImageContext()
    }

However, in my image view, only dots are rendered:

enter image description here

I'm probably misunderstanding something about how UIBezierPath works; any help appreciated!


Solution

  • The UIBezierPath documentation explains the meaning of the pattern argument:

    A C-style array of floating point values that contains the lengths (measured in points) of the line segments and gaps in the pattern. The values in the array alternate, starting with the first line segment length, followed by the first gap length, followed by the second line segment length, and so on.

    In your code, 0.001 is the first value in the pattern, so it is a line segment length, and is small enough to be drawn as a dot. The second value in the pattern is path.lineWidth * 2, so it is a gap length, which is the distance between the dots.

    If you want to alternate dots and dashes, you have to pass a pattern containing four numbers: a line segment length, a gap, another line segment length, and another gap.

    let dots: [CGFloat] = [
        0.001,              // dot
        path.lineWidth * 2, // gap
        path.lineWidth * 2, // dash
        path.lineWidth * 2, // gap
    ]