IBDesignable
dotted line
iOS development
Swift programming
2017 tutorial

Draw dotted not dashed line, with IBDesignable in 2017

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

A dotted line in UIKit is usually just a dashed stroke with a very short dash length and a round line cap. IBDesignable makes that custom view visible in Interface Builder, which is useful when designers and developers want to adjust spacing, color, and thickness without rebuilding the app each time.

Why CAShapeLayer Is the Right Tool

You can draw dots in draw(_:), but CAShapeLayer is simpler and usually performs better for a reusable line view. It also fits well with IBDesignable because the layer can be updated whenever Interface Builder re-renders the view.

A practical implementation uses one reusable shape layer:

swift
1import UIKit
2
3@IBDesignable
4final class DottedLineView: UIView {
5    private let shapeLayer = CAShapeLayer()
6
7    @IBInspectable var lineColor: UIColor = .systemGray {
8        didSet { updatePath() }
9    }
10
11    @IBInspectable var lineWidth: CGFloat = 2 {
12        didSet { updatePath() }
13    }
14
15    @IBInspectable var dotSpacing: CGFloat = 6 {
16        didSet { updatePath() }
17    }
18
19    override init(frame: CGRect) {
20        super.init(frame: frame)
21        commonInit()
22    }
23
24    required init?(coder: NSCoder) {
25        super.init(coder: coder)
26        commonInit()
27    }
28
29    override func layoutSubviews() {
30        super.layoutSubviews()
31        updatePath()
32    }
33
34    override func prepareForInterfaceBuilder() {
35        super.prepareForInterfaceBuilder()
36        commonInit()
37        updatePath()
38    }
39
40    private func commonInit() {
41        if shapeLayer.superlayer == nil {
42            layer.addSublayer(shapeLayer)
43        }
44        backgroundColor = .clear
45    }
46
47    private func updatePath() {
48        let path = UIBezierPath()
49        let y = bounds.midY
50        path.move(to: CGPoint(x: 0, y: y))
51        path.addLine(to: CGPoint(x: bounds.width, y: y))
52
53        shapeLayer.frame = bounds
54        shapeLayer.path = path.cgPath
55        shapeLayer.strokeColor = lineColor.cgColor
56        shapeLayer.fillColor = nil
57        shapeLayer.lineWidth = lineWidth
58        shapeLayer.lineCap = .round
59        shapeLayer.lineDashPattern = [1, NSNumber(value: Float(dotSpacing))]
60    }
61}

The key settings are lineCap = .round and a tiny first dash segment. That combination makes the stroke appear as dots instead of rectangular dashes.

Making It Work in Interface Builder

Once the class is assigned to a UIView in a storyboard or XIB, the @IBInspectable properties appear in the Attributes Inspector. That lets you tune the line width and spacing visually.

Because Interface Builder redraws often, it is important not to add a new shape layer every time layoutSubviews runs. The example creates the layer once and only updates its path and appearance afterward.

If you want a vertical dotted line, build the path vertically instead of horizontally:

swift
1private func updateVerticalPath() {
2    let path = UIBezierPath()
3    let x = bounds.midX
4    path.move(to: CGPoint(x: x, y: 0))
5    path.addLine(to: CGPoint(x: x, y: bounds.height))
6    shapeLayer.path = path.cgPath
7}

Why Some Dashed Lines Do Not Look Like Dots

Developers often try a dash pattern such as [2, 2] and expect dots. That usually produces short dashes, not dots, because the stroke ends are square by default.

To make the result look dotted, combine these rules:

  • use a round line cap
  • make the dash segment very short, often 1
  • pick a spacing value larger than the line width

The visual effect depends on all three.

Supporting Live Resizing

Interface Builder and Auto Layout can resize the view after initialization, so path creation should happen in layoutSubviews, not only in init. That ensures the line stretches to the actual final width.

If you expose many customizable properties, calling updatePath() in each didSet keeps preview rendering in sync with property changes.

Common Pitfalls

A common mistake is adding a fresh CAShapeLayer every time the view lays out. That creates stacked layers and eventually hurts rendering performance.

Another issue is forgetting lineCap = .round. Without it, the result looks dashed even if the dash length is very small.

Some implementations put the drawing code only in draw(_:) and then wonder why Interface Builder previews are inconsistent. A dedicated shape layer updated from layout is usually more predictable.

Finally, make sure the view has enough height. If the height is smaller than the line width, the dots can appear clipped or blurry.

Summary

  • Use CAShapeLayer for a clean reusable dotted line view.
  • Combine a short dash pattern with a round line cap to create dots.
  • Update the path in layoutSubviews so Auto Layout changes are handled.
  • Expose spacing, width, and color with @IBInspectable.
  • Reuse one shape layer instead of adding new layers repeatedly.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.