A lot of times we start to search third party libraries to include a simple chart in our iOS app instead to develop our own code to solve the problem.
Maybe the third party library configuration works in that case but sometimes because of design decisions you have to make modifications of third party library and apply some workaround to adjust to your iOS app. In that times you think, what takes more time? and what is the more maintainable option?.
That happened to me in a project and I started to research about the difficulty to develop my own solution. And voila using UIStackView objects I developed my solution in a short time.
What is a UIStackView?
It’s an optimized interface to show a collection view disposed in a column or in a row. For our case the easy way to understand is that each bar chart is an UIView object. If we can add one collection of these objects to an UIStackView disposed in a row with equivalent spacing we have a simple bar chart.
Example: Running App
For this example we will develop a bar char to show the running time in the last six months. This bar chart can be uses in the stats section of training app. The bar chart will be like the image, with a bottom index and value indicator in each bar.
Really this bar chart is formed by two UIStackView objects as containers where we applied some configurations. In our case that configurations will be very similar. The stack 1, we called GraphStackView, will contain the chart bar and the stack 2, we called IndexStackView, will contain the index UILabels.
The GraphStackView has the peculiarity that each element will be an vertical UIStackView that contain an UIView and an UILabel to indicate the time.
Both, GraphStackView and IndexStackView will be define in the Interface Builder and the UIStackView objects contained inside of the GraphStackView will be develop programatically.
We start with Interface Builder
In a Storyboard view we create two horizontal UIStackView objects.
The IndexGraphView is a horizontal UIStackView with a separation between the elementos of 20px and a equality distribution, that means that every elements inside the IndexGraphView will have the same width.
The GraphStackView has the same properties that the IndexStackView but with more heigth.
Data type.
In order to show the time used monthly we use a simple structure: TimeGraphData
struct TimeGraphData { var order: Int var amount: String var month: String var percentage: Double init (order: Int, amount: String, month: String, percentage: Double) { self.order = order self.amount = amount self.month = month self.percentage = percentage } }
Each TimeGraphData element render a bar item of the bar chart. In the final example we will loop the TimeGraphData collection and we will create the related UIStackView objects and it’s elements.
IndexStackView
The IndexStackView will represent the months list set in the bottom. Inside of this stack we will add each of UILabels with the month name.
This is the code to make each UILabel element of the IndexStackView. Each UILabel will set a height inside the IndexStackView with the “heightAnchor” property.
private func addIndexElement (timeGraphData: TimeGraphData) { let monthLabelHeight: CGFloat = 13.0 let monthLabel = UILabel() monthLabel.text = timeGraphData.month monthLabel.font = UIFont.systemFont(ofSize: monthLabelHeight) monthLabel.textAlignment = .center monthLabel.heightAnchor.constraint(equalToConstant: monthLabelHeight).isActive = true indexStackView.addArrangedSubview(monthLabel) indexStackView.translatesAutoresizingMaskIntoConstraints = false; }
GraphStackView
Create the content of this stack will be more complex. The GraphStackView contain each vertical UIStackView developed programatically that will be formed by an UIView and an UILabel.
This is the code to add each item:
private func addGraphElement (timeGraphData: TimeGraphData) { let amountLabelFontSize: CGFloat = 9.0 let amountLabelPadding: CGFloat = 15.0 let height = heightPixelsDependOfPercentage(percentage: timeGraphData.percentage) let totalHeight = height + amountLabelPadding let verticalStackView: UIStackView = UIStackView() verticalStackView.axis = .vertical verticalStackView.alignment = .fill verticalStackView.distribution = .fill verticalStackView.spacing = 8.0 let amountLabel = UILabel() amountLabel.text = timeGraphData.amount amountLabel.font = UIFont.systemFont(ofSize: amountLabelFontSize) amountLabel.textAlignment = .center amountLabel.textColor = UIColor.darkText amountLabel.adjustsFontSizeToFitWidth = true amountLabel.heightAnchor.constraint(equalToConstant: amountLabelFontSize).isActive = true let view = UIView() view.backgroundColor = graphColor view.heightAnchor.constraint(equalToConstant: height).isActive = true verticalStackView.addArrangedSubview(amountLabel) verticalStackView.addArrangedSubview(view) verticalStackView.heightAnchor.constraint(equalToConstant: totalHeight).isActive = true verticalStackView.translatesAutoresizingMaskIntoConstraints = false; graphStackView.addArrangedSubview(verticalStackView) graphStackView.translatesAutoresizingMaskIntoConstraints = false; }
The method “heighPixelDependOfPercentage” return the height of the vertical stack in pixels.
We create the “verticalStackView” with vertical orientation, and fill distribution with a space of 8 px between elementos.
In the same way that the previews stack both, UILabel and UIView set the height inside the stack using the “heightAnchor” property.
Download de complete project
In this article I have showed the key parts of the code to make a simple bar chart with UIStackView but here it’s available the complete project.
Conclusion
With this code in a simple way we can integrate a bar chart in our app without use a third party library. If we dedicate some time we can improve this example in order to add more elements, clickable bars, scrolling bars, etc.
And you, Do you often prefer use a third party library or develop your own controls?
Related articles:
Petrol App – Integration with CARTO in a Mobile App
Hi Gonzalo,
Thanks for posting this, I was trying to build a bargraph to be used in a tableview for each cell and your solution was just a very good idea !!
Cheers,
Chris Folkers
New Zealand