Shahzad Ali
Shahzad Ali

Reputation: 123

circle with dash lines uiview

I am trying to make a circle with dash lines. I am able to make lines in rectangle but I don't know how to make these in circle. Here is answer I got but it's in Objective-C: UIView Draw Circle with Dotted Line Border

Here is my code which makes a rectangle with dashed lines.

func addDashedBorder() {
    let color = UIColor.red.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 = color
    shapeLayer.lineWidth = 2
    shapeLayer.lineJoin = CAShapeLayerLineJoin.round
    shapeLayer.lineDashPattern = [6,3]
    shapeLayer.path = UIBezierPath(roundedRect: shapeRect, cornerRadius: 5).cgPath

    self.layer.addSublayer(shapeLayer)
}

Upvotes: 7

Views: 4979

Answers (3)

RajeshKumar R
RajeshKumar R

Reputation: 15768

You can use UIBezierPath(ovalIn:) to create a circle path in a square view.

extension UIView {
    func addDashedCircle() {
        let circleLayer = CAShapeLayer()
        circleLayer.path = UIBezierPath(ovalIn: bounds).cgPath
        circleLayer.lineWidth = 2.0
        circleLayer.strokeColor =  UIColor.red.cgColor//border of circle
        circleLayer.fillColor = UIColor.white.cgColor//inside the circle
        circleLayer.lineJoin = .round
        circleLayer.lineDashPattern = [6,3]
        layer.addSublayer(circleLayer)
    }
}

Set view background color .clear and fill color of the layer .white

class View1: UIViewController {
    @IBOutlet weak var circleView: UIView!
    override func viewDidLoad() {
        super.viewDidLoad()
        circleView.backgroundColor = .clear//outside the circle
        circleView.addDashedCircle()
    }
}

Or using UIBezierPath(arcCenter:radius:startAngle:endAngle:clockwise:)

circleLayer.path = UIBezierPath(arcCenter: CGPoint(x: frame.size.width/2, y: frame.size.height/2),
                            radius: min(frame.size.height,frame.size.width)/2,
                            startAngle: 0,
                            endAngle: .pi * 2,
                            clockwise: true).cgPath

enter image description here

Upvotes: 3

Rob
Rob

Reputation: 437917

Certainly you can just render your circular UIBezierPath with the selected dash pattern:

class DashedCircleView: UIView {
    private var shapeLayer: CAShapeLayer = {
        let shapeLayer = CAShapeLayer()
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineWidth = 10
        shapeLayer.lineCap = .round
        shapeLayer.lineDashPattern = [20, 60]
        return shapeLayer
    }()

    override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        configure()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        updatePath()
    }
}

private extension DashedCircleView {
    func configure() {
        layer.addSublayer(shapeLayer)
    }

    func updatePath() {
        let rect = bounds.insetBy(dx: shapeLayer.lineWidth / 2, dy: shapeLayer.lineWidth / 2)
        let radius = min(rect.width, rect.height) / 2
        let center = CGPoint(x: rect.midX, y: rect.midY)
        let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
        shapeLayer.path = path.cgPath
    }
}

That yields:

enter image description here

The problem with that approach is that it’s hard to get the dashed pattern to line up (notice the awkward dashing at the “3 o’clock” position). You can fix that by making sure that the two values of lineDashPattern add up to some number that evenly divides into the circumference of the circle (e.g. 2π × radius):

let circumference: CGFloat = 2 * .pi * radius
let count = 30
let relativeDashLength: CGFloat = 0.25
let dashLength = circumference / CGFloat(count)
shapeLayer.lineDashPattern = [dashLength * relativeDashLength, dashLength * (1 - relativeDashLength)] as [NSNumber]

Alternatively, rather than using lineDashPattern at all, you can instead keep a solid stroke and make the path, itself, as a series of small arcs. That way I can achieve the desired dashed effect, but ensuring it’s evenly split into count little arcs as we rotate from 0 to 2π:

class DashedCircleView: UIView {
    private var shapeLayer: CAShapeLayer = {
        let shapeLayer = CAShapeLayer()
        shapeLayer.strokeColor = UIColor.red.cgColor
        shapeLayer.fillColor = UIColor.clear.cgColor
        shapeLayer.lineWidth = 10
        shapeLayer.lineCap = .round
        return shapeLayer
    }()

    override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        configure()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        updatePath()
    }
}

private extension DashedCircleView {
    func configure() {
        layer.addSublayer(shapeLayer)
    }

    func updatePath() {
        let rect = bounds.insetBy(dx: shapeLayer.lineWidth / 2, dy: shapeLayer.lineWidth / 2)
        let radius = min(rect.width, rect.height) / 2
        let center = CGPoint(x: rect.midX, y: rect.midY)

        let path = UIBezierPath()
        let count = 30
        let relativeDashLength: CGFloat = 0.25 // a value between 0 and 1
        let increment: CGFloat = .pi * 2 / CGFloat(count)

        for i in 0 ..< count {
            let startAngle = increment * CGFloat(i)
            let endAngle = startAngle + relativeDashLength * increment
            path.move(to: CGPoint(x: center.x + radius * cos(startAngle), 
                                  y: center.y + radius * sin(startAngle)))
            path.addArc(withCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
        } 
        shapeLayer.path = path.cgPath
    }
}

That yields:

enter image description here

Upvotes: 12

Cole James
Cole James

Reputation: 230

Draw the path using the circle path variant of UIBezierPath

shapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: frame.size.width * 0.5, y: frame.size.height * 0.5), radius: frame.size.width * 0.5, startAngle: 0, endAngle: .pi * 2, clockwise: true)

Upvotes: 0

Related Questions