Draw dotted (not dashed!) line, with IBDesignable in 2017

40,295

Solution 1

Set the line cap style to round and set the “on” length to a tiny number.

Swift playground example:

import UIKit
import PlaygroundSupport

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

let dashes: [CGFloat] = [0.001, path.lineWidth * 2]
path.setLineDash(dashes, count: dashes.count, phase: 0)
path.lineCapStyle = CGLineCap.round

UIGraphicsBeginImageContextWithOptions(CGSize(width:300, height:20), false, 2)

UIColor.white.setFill()
UIGraphicsGetCurrentContext()!.fill(.infinite)

UIColor.black.setStroke()
path.stroke()

let image = UIGraphicsGetImageFromCurrentImageContext()
let view = UIImageView(image: image)
PlaygroundPage.current.liveView = view

UIGraphicsEndImageContext()

Result:

dots


For objective-C, using the same example class as in the question, simply add

CGContextSetLineCap(cx, kCGLineCapRound);

before the call to CGContextStrokePath, and change the ra array values to match my Swift code.

Solution 2

Objective-C version of the Swift example above:

UIBezierPath * path = [[UIBezierPath alloc] init];
[path moveToPoint:CGPointMake(10.0, 10.0)];
[path addLineToPoint:CGPointMake(290.0, 10.0)];
[path setLineWidth:8.0];
CGFloat dashes[] = { path.lineWidth, path.lineWidth * 2 };
[path setLineDash:dashes count:2 phase:0];
[path setLineCapStyle:kCGLineCapRound];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 20), false, 2);
[path stroke];
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Solution 3

Using a UIView extension, compatible with Swift 3.0 the following should work:

extension UIView {

    func addDashedBorder(strokeColor: UIColor, lineWidth: CGFloat) {
        self.layoutIfNeeded()
        let strokeColor = strokeColor.cgColor

        let shapeLayer:CAShapeLayer = CAShapeLayer()
        let frameSize = self.frame.size
        let shapeRect = CGRect(x: 0, y: 0, width: frameSize.width, height: frameSize.height)

        shapeLayer.bounds = shapeRect
        shapeLayer.position = CGPoint(x: frameSize.width/2, y: frameSize.height/2)
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.strokeColor = strokeColor
        shapeLayer.lineWidth = lineWidth
        shapeLayer.lineJoin = kCALineJoinRound

        shapeLayer.lineDashPattern = [5,5] // adjust to your liking
        shapeLayer.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: shapeRect.width, height: shapeRect.height), cornerRadius: self.layer.cornerRadius).cgPath

        self.layer.addSublayer(shapeLayer)
    }

}

Then in a function that runs after viewDidLoad, like viewDidLayoutSubviews, run the addDashedBorder function on the view in question:

class ViewController: UIViewController {

    var someView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        someView = UIView()
        someView.layer.cornerRadius = 5.0

        view.addSubview(someView)

        someView.translatesAutoresizingMaskIntoConstraints = false
        someView.widthAnchor.constraint(equalToConstant: 200).isActive = true
        someView.heightAnchor.constraint(equalToConstant: 200).isActive = true
        someView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        someView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }

    override func viewDidLayoutSubviews() {
        someView.addDashedBorder(strokeColor: UIColor.red, lineWidth: 1.0)
    }

}

Solution 4

Hello guys this solution worked for me fine. I found somewhere and changed a bit to prevent console warnings.

extension UIImage {
    static func drawDottedImage(width: CGFloat, height: CGFloat, color: UIColor) -> UIImage {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 1.0, y: 1.0))
        path.addLine(to: CGPoint(x: width, y: 1))
        path.lineWidth = 1.5           
        let dashes: [CGFloat] = [path.lineWidth, path.lineWidth * 5]
        path.setLineDash(dashes, count: 2, phase: 0)
        path.lineCapStyle = .butt
        UIGraphicsBeginImageContextWithOptions(CGSize(width: width, height: height), false, 2)
        color.setStroke()
        path.stroke()

        let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return image
    }
}

This is the result:

result

Solution 5

I work a bit on rob mayoff accepted solution to easily customize the dotted line:

  • change the radius of each circle.
  • change the number of spaces between 2 circles.
  • change the number of patterns to generate.

The function return an UIImage:

extension UIImage {

    class func dottedLine(radius radius: CGFloat, space: CGFloat, numberOfPattern: CGFloat) -> UIImage {


        let path = UIBezierPath()
        path.moveToPoint(CGPointMake(radius/2, radius/2))
        path.addLineToPoint(CGPointMake((numberOfPattern)*(space+1)*radius, radius/2))
        path.lineWidth = radius

        let dashes: [CGFloat] = [path.lineWidth * 0, path.lineWidth * (space+1)]
        path.setLineDash(dashes, count: dashes.count, phase: 0)
        path.lineCapStyle = CGLineCap.Round


        UIGraphicsBeginImageContextWithOptions(CGSizeMake((numberOfPattern)*(space+1)*radius, radius), false, 1)
        UIColor.whiteColor().setStroke()
        path.stroke()
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image

    }
}

And here is how to get the image:

UIImage.dottedLine(radius: 100, space: 2, numberOfPattern: 1)
Share:
40,295
Fattie
Author by

Fattie

Updated on November 29, 2021

Comments

  • Fattie
    Fattie over 2 years

    It's easy to draw a dashed line with UIKit. So:

    CGFloat dashes[] = {4, 2};
    [path setLineDash:dashes count:2 phase:0];
    [path stroke];
    

    enter image description here

    Is there any way way to draw a genuine dotted line?

    enter image description here

  • rob mayoff
    rob mayoff over 9 years
    The key information is in the first (English text) line of my answer. The rest is gravy.
  • helloB
    helloB almost 8 years
    this is producing squares, not circles, when I use your code.
  • iAkshay
    iAkshay almost 8 years
    try changing moveToPoint, addLineToPoint,linewidth and lineDashPattern values as per your requirement
  • James P
    James P over 7 years
    I've found setting the on length to 0.01 gives you a circular dot, whereas they are slightly elongated when using 0.
  • rob mayoff
    rob mayoff over 7 years
    I created that image by taking a screen shot (default system-wide shortcut: ⌘⇧4). There's never been a built-in capture facility in Xcode that I know of.
  • Crashalot
    Crashalot over 7 years
    This creates a dashed line (i.e., rectangles), but how do you create a dotted (i.e., circles) line?
  • Womble
    Womble about 6 years
    James P's advice is invaluable. This bug has caused me so much heart-ache and work. Thank you James. I'll create a semi-Answer, so people can see it more clearly.
  • rob mayoff
    rob mayoff about 6 years
    I've updated my answer with the bug workaround and to the latest Swift syntax.
  • Fattie
    Fattie about 6 years
    It occurs to me, there's no .smallestFiniteMagnitude !
  • Nomanur
    Nomanur almost 3 years
    I like this easy way