 # Half-Circle Progress Indicator on Swift. How to draw a half-circle with UIBezierPath

I want to share my experience in custom progress indicator creation. I will show how to create a custom progress indicator drawn with UIBezierPath like bellow:

So let's start with the idea. We need to create 3 layers (CALayer) for the first indicator, and 2 layers for the second indicator. The main trick that 3 half-circles just clip each other like bellow:

This is a full class for Indicator. It has 2 modes - with the inner circle and without it, like on a first gif.

And now I will explain how it works.

Let's discuss getOutherGrayCircle function. In this code, we have 3 functions for each half-circle. As they all are half-circles, then we need to understand how to draw only the first half-circle and 2 others will be similarly drawn.

``````func getOutherGrayCircle() -> CAShapeLayer {
let center = CGPoint(x: fullSize.width / 2, y: fullSize.height)
let beizerPath = UIBezierPath()
beizerPath.move(to: center)
startAngle: .pi,
endAngle: 2 * .pi,
clockwise: true)
beizerPath.close()
let innerGrayCircle = CAShapeLayer()
innerGrayCircle.path = beizerPath.cgPath
innerGrayCircle.fillColor = UIColor.gray.cgColor
return innerGrayCircle
}``````

The center point of UIBezierPath is a center point by X and max Y on the view (center of the coordinate system on the image below). startAngle is always .pi and endAngle we need to calculate ( it depend on how many percents the indicator should show). endAngle has a limit in 2 * .pi

So right now we can create one half-circle. Two grays half-circles will be static, and that's why startAngle and endAngle in getOutherGrayCircle and getInnerGrayCircle equals to .pi and 2 * .pi.

We have only 1 dynamic half-circle - getGreenCircle. Here we need to set endAngle to depend on how many percents indicator should show. For example: If we need 60% on the indicator, then:

endAngle = .pi + .pi * 0.6

Now we can create all 3 circles and add them to the parent layer

``````func drawShape(bounds: CGRect) {
fullSize = bounds.size
grayCircleSize = fullSize
greenCircleSize = CGSize(width: bounds.width - 6.0, height: bounds.width - 6.0)
innerGrayCircleSize = CGSize(width: greenCircleSize.width - 44.0,
height: greenCircleSize.width - 44.0)
let outerCicrcle = getOutherGrayCircle()
let greenCircle = getGreenCircle()
progressLayer = greenCircle
if isInnerCircleExist {
let innerGrayCircle = getInnerGrayCircle()
}

}``````

For indicator without inner gray half-circle, we just don't need to draw this layer. And that's it.

Github project