I have a custom UIView class which I use to draw an arc. What I want to achieve is to add labels around this arc as gauge reading. Here is the output I am trying to achieve
I am able to draw the arc but I am not sure how can I add labels to it. This is the code I am using to create the arc.
Custom UIView class
open class Gauge: UIView {
var gaugeLayer: CALayer!
var ringLayer: CAShapeLayer!
private let uLabel = UILabel()
func updateLayerProperties() {
backgroundColor = UIColor.clear
if (ringLayer != nil) {
ringLayer.strokeEnd = 0.75
}
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
updateLayerProperties()
addLabel()
}
public override init(frame: CGRect) {
super.init(frame: frame)
updateLayerProperties()
addLabel()
}
open override func draw(_ rect: CGRect) {
super.draw(rect)
updateLayerProperties()
}
func resetLayers() {
layer.sublayers = nil
ringLayer = nil
}
open override func layoutSubviews() {
resetLayers()
gaugeLayer = getCircleGauge(rotateAngle)
layer.addSublayer(gaugeLayer)
updateLayerProperties()
}
func addLabel() {
uLabel.font = //font
uLabel.textColor = //color
addSubview(uLabel)
}
}
Code for arc
func getCircleGauge() -> CAShapeLayer {
let gaugeLayer = CAShapeLayer()
if ringLayer == nil {
ringLayer = CAShapeLayer.getOval(lineWidth, strokeStart: 0, strokeEnd: 0.75, strokeColor: UIColor.clear, fillColor: UIColor.clear, shadowRadius: shadowRadius, shadowOpacity: shadowOpacity, shadowOffsset: CGSize.zero, bounds: bounds)
ringLayer.frame = layer.bounds
gaugeLayer.addSublayer(ringLayer)
}
gaugeLayer.frame = layer.bounds
gaugeLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
gaugeLayer.transform = CATransform3DRotate(gaugeLayer.transform, CGFloat(rotateAngle * 2 - pi_2 * 5 / 2), 0, 0, 1)
ringLayer.lineCap = .round
return gaugeLayer
}
When I tried adding UILabel in the custom view in its init the app crashed
view.superview is nil during traversal after it has appeared in superview subviews
I am not sure what is the issue? Do I add UILabels or is there any other way to do it.

There are a number of different ways to do this... you could add
UILabels, or you could useCATextLayer... depending on what else you might want to do with this could affect how you want to go about it.First, your code is doing a few things it shouldn't do, so let's start with cleaning that up a bit.
Instead of using an oval with
.strokeEnd: 0.75, let's use an arc that is 75% of a full circle (which is 270º), with the "gap" at the bottom. 0º (zero) is at "3 o'clock", so we define a clockwise arc from 135º (90º + 45º) to 405º (135º + 270º):This will also come in handy later when positioning the labels.
Most people find it easier to think in terms of degrees rather than
.pi, and "circle math" uses radians, we'll use this common extension to convert between them:So our starting point is to write a "basic"
UIViewsubclass:and a simple controller to show it:
That gives us this output:
You'll notice the arc extends outside the bounds of the view... and, we'll be adding labels around the outside, so we'll need to make the radius smaller than one-half of the view width. But we'll get to that shortly.
The next step is to add labels. We'll start with just one.
Very little difference from
BasicGuageclass:We've added a
UILabeland centered it in the view, so (using that same view controller class), it now looks like this:We will, of course, need more than one label, so we'll eventually use an array of
[UILabel].To position the labels around the outside of the arc, we can define (but not draw) a circle with a larger radius, and then use a common extension for finding the point on the circle:
The process will be:
Here's the idea:
We're adding two values - 20 and 30. The degree-range is 270º.
20 is 20% of 100, so we use the starting angle of 135 plus 20% of 270 (54):
30 is 30% of 100, so we use the starting angle of 135 plus 30% of 270 (81):
so the "20" label goes on the dashed-circle radius at
189ºand the "30" label goes at216º.Here's an example
Gaugeview class that has the sizing and label placement:and an example view controller class:
Tapping anywhere cycles through some sample value sets:
and so on.
Note: This is Sample Code Only ... and all examples assume a square (
1:1ratio) view.