In today’s post we will go through the custom UI element ScaleView. With simple implementation, you can get a fully custom view that acts as a scrollable scale. The idea behind this control was to get a custom UI that will receive min/max scale number, and then according to the difference and index of the big line, draw the lines similar to weight scale.

Solution

The hardest thing about this custom UI control is deciding which way the lines will be drawn and how many of them. Only viable solution is to first draw used scale lines, which have a starting point at the middle of the view. After used lines are drawn and the right side of unused lines, continue with drawing of the left side of unused line to fill up the empty part of screen.

        for index in 0 ... (numberOfUnusedLines + numberOfUsedLines*unusedLinesMultiplier + numberOfUsedLines%self.scalingValue)
{
drawScaleLine(offset: linesWithSpaceWidth*(index) + Int(self.bounds.size.width/2), color:(index > numberOfUsedLines) ? unusedLinesColor : usedLinesColor, addLabel:numberOfUsedLines >= index, bigLine: (index + Int(minValue))%self.scalingValue == 0)
}
for index in 1 ... numberOfUnusedLines*unusedLinesMultiplier
{
drawScaleLine(offset: Int(self.bounds.size.width/2) - linesWithSpaceWidth*(index), color:unusedLinesColor, addLabel: false, bigLine: (index + self.scalingValue - Int(minValue))%self.scalingValue == 0)
}
self.scaleContainerView.setNeedsDisplay()
self.scaleContainerView.layoutIfNeeded()
self.scaleContainerView.frame = CGRect(x: 0, y: 0, width: linesWithSpaceWidth*numberOfUsedLines + Int(self.frame.size.width), height: Int(self.frame.size.height))
scaleScrollView.addSubview(self.scaleContainerView)
scaleScrollView.contentSize = self.scaleContainerView.frame.size

After the lines are drawn, the view, that contains the lines, is added as subview in ScrollView and then his size is used as content size for the scaleScrollView.

Implementation

Now that we have the solution for drawing line lets, start with adding other customizable parts.

    var numberOfUsedLines: Int = 10
var numberOfUnusedLines: Int = Int(UIScreen.main.bounds.size.width/2)/5
var minValue: Float = 0
var maxValue: Float = 0
var lineWidth = 1
var linesSpacing = 4
var usedLinesColor = UIColor.black
var unusedLinesColor = UIColor.lightGray
var mainLinesLabelPercentHeightValue = 0.5
var subLinesLabelPercentHeightValue = 0.75
var scalingValue = 5
var indicatorWidth: CGFloat = 1
var indicatorColor: UIColor = UIColor.blue
var scaleLabelFont = UIFont(name: "Futura", size: 8)
var scaleLabelColor = UIColor.green
var shouldShowHorizontalScrollIndicator = false
var useBounces = false

The main parameters are min and max variables, which will determine the number of drawn lines in scale (numberOfUsedLines).
linesWidth is used for width of drawn lines in pixels.
linesSpacing is used for distance between drawn lines.
usedLinesColor, unusedLinesColor, mainLinesLabelPercentHeightValue and subLinesLabelPercentHeightValue are used to set the color/heigt of drawn lines
indicatorWidth and indicatorColor are used width/colour of vertical line indicator
scalingValue is used to determine the index when the big line will be drawn
scaleLabelFont and scaleLabelColor are used for changing the style of labels added over the big lines
shouldShowHorizontalScrollIndicator, disable/enable scroll indicator
useBounces, disable/enable bounces

All of the variables have predefined default values. After changing any of these parameters, the method reloadScaleData() needs to be called, so the changes will be applied to scaleView and the line will be redrawn.
The implementation is pretty simple, because ScaleView allows adding programmatically as a subview, and through .xib/.storyboard
In the example we will add it through .storyboard

View is added to storyboard as class ScrollableScaleView and connected as variable testScrollableView.

  @IBOutlet weak var testScrollableView: ScrollableScaleView!

After building/running the project we can start adding custom values for this scale view.

 testScrollableView.minValue = min
testScrollableView.maxValue = max
testScrollableView.lineWidth = width
testScrollableView.indicatorWidth = CGFloat(3)
testScrollableView.linesSpacing = spacing
testScrollableView.indicatorColor =  UIColor.init(red: 118.0/255.0, green: 221.0/255.0, blue: 251.0/255.0, alpha: 1)
testScrollableView.usedLinesColor = UIColor.init(red: 23.0/255.0, green: 39.0/255.0, blue: 81.0/255.0, alpha: 0.12)
testScrollableView.unusedLinesColor = UIColor.init(red: 23.0/255.0, green: 39.0/255.0, blue: 81.0/255.0, alpha: 0.05)
testScrollableView.shouldShowHorizontalScrollIndicator = false
testScrollableView.reloadScaleData()
testScrollableView.updateScaleLabelsWith(newFont: UIFont(name: "Futura", size: 10)!, newColor: UIColor.init(red: 23.0/255.0, green: 39.0/255.0, blue: 81.0/255.0, alpha: 0.12))

Values min, max, width, spacing are used from the designated textField-s

Conclusion

Working on this custom UI element was quite challenging and fun since there was a need for trying different approaches and solutions to get desired UI behavior.