iOS Swift Chart : Bar chart background colour - ios

I create a bar chart that works completely fine. How can I add the bar chart capsule background color? Not want the entire chart background color but I want to need a background color for each bar so it will look like fill and unfill like effet to the bar chat.
var BAR_WIDTH_CONSTANT: CGFloat = 3
var BAR_DISTANCE: CGFloat = 14
var BAR_MAX_HEIGHT: CGFloat = 50
let MAX_VALUE: CGFloat = 60
let MIN_VALUE: CGFloat = 0
var unitsSold = [20.0, 4.0, 6.0, 3.0, 12.0, 16.0, 4.0]
var months = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
var graphType = 1
var maxValue = 30.0
func setChart(arrX:[String]) {
let customFormater = BarChartFormatter()
customFormater.months = self.months
viewChart.xAxis.valueFormatter = customFormater
self.viewChart.xAxis.labelPosition = XAxis.LabelPosition.bottom
self.viewChart.setVisibleXRangeMaximum(Double(arrX.count))
self.viewChart.fitScreen()
var dataEntries = [BarChartDataEntry]()
for i in 0..<months.count {
let dataEntry = BarChartDataEntry(x: Double(i), y: Double(unitsSold[i]), data: months as AnyObject?)
dataEntries.append(dataEntry)
}
let chartDataSet = BarChartDataSet(entries: dataEntries, label: "")
chartDataSet.valueTextColor = .clear
chartDataSet.valueFont = themeFont(size: 9, fontname: .Poppins_Black)
chartDataSet.colors = [.appThemeColor, UIColor(named: "DarkGreen")!]
//Remove 0 value from graphs
let noZeroFormatter = NumberFormatter()
noZeroFormatter.zeroSymbol = ""
chartDataSet.valueFormatter = DefaultValueFormatter(formatter: noZeroFormatter)
chartDataSet.barShadowColor = .clear
let chartData = BarChartData(dataSet: chartDataSet)
chartData.barWidth = 0.6
// if graphType == 1 || graphType == 3 {
// chartData.barWidth = 0.4
// }
viewChart.data = chartData
viewChart.animate(xAxisDuration: 1, yAxisDuration: 1, easingOption: .linear)
}
func setupChart() {
viewChart.delegate = self
viewChart.chartDescription.enabled = false
viewChart.dragEnabled = true
viewChart.doubleTapToZoomEnabled = false
viewChart.pinchZoomEnabled = false
viewChart.xAxis.valueFormatter = self
viewChart.legend.enabled = false
viewChart.setScaleEnabled(false)
viewChart.isUserInteractionEnabled = false
let leftAxisFormatter = NumberFormatter()
leftAxisFormatter.maximumFractionDigits = 0
let xAxis = viewChart.xAxis
xAxis.drawGridLinesEnabled = true
xAxis.labelPosition = .topInside
xAxis.labelRotationAngle = 0
xAxis.labelFont = themeFont(size: 12, fontname: .Poppins_Light)
xAxis.labelTextColor = .darkGray
xAxis.granularity = 1
xAxis.axisLineWidth = 0
xAxis.labelCount = 12
xAxis.granularityEnabled = true
xAxis.valueFormatter = self
xAxis.labelRotationAngle = 0
xAxis.gridColor = .clear
let yAxis = viewChart.leftAxis
yAxis.drawGridLinesEnabled = false
yAxis.axisMinimum = 0
yAxis.axisMaximum = Double(maxValue)
yAxis.axisLineWidth = 0
yAxis.labelTextColor = .clear
yAxis.drawLabelsEnabled = true
yAxis.labelPosition = .outsideChart
let dAxis = viewChart.rightAxis
dAxis.drawGridLinesEnabled = false
dAxis.axisMinimum = 0
dAxis.axisLineWidth = 0
dAxis.labelTextColor = .clear
dAxis.drawLabelsEnabled = true
dAxis.labelPosition = .outsideChart
}
This is what I'm looking for.
This is what I have done yet.

if you are drwaing your BarChart using Charts, then to apply corner radius you should have to change the internal code of the Charts Framework.
You can do it as:
Search (Shift+Command+O) for the BarChartRenderer.swift (Shift+Command+O) file and replace the (every)line
context.fill(barRect)
with the code below
let bezierPath = UIBezierPath(roundedRect: barRect, cornerRadius: 3.0)
context.addPath(bezierPath.cgPath)
context.drawPath(using: .fill)

Related

How to create a limit line for each bar chart in iOS swift

How to create a limit line for each bar chart in iOS swift. I'm using Chart Library
Here is my code: -
var chartView: BarChartView!
var entries: [BarChartDataEntry] = []
var data: [Double]?
func draw() {
for index in 0..<data.count {
let dataEntry = BarChartDataEntry(x: Double(index), y: data[index])
entries.append(dataEntry)
}
let chartDataSet = BarChartDataSet(values: entries, label: "")
let chartData = BarChartData(dataSet: chartDataSet)
chartData.setValueFont(UIFont(name: "Roobert-SemiBold", size: 12.0))
chartData.barWidth = 0.3
chartView.data = chartData
var limitLine = ChartLimitLine()
limitLine = ChartLimitLine(limit: 15, label: "15")
limitLine.lineColor = .blue
limitLine.valueTextColor = .blue
limitLine.lineDashLengths = [3.0]
chartView.leftAxis.addLimitLine(limitLine)
var limitLine = ChartLimitLine()
limitLine = ChartLimitLine(limit: 12, label: "12")
limitLine.lineColor = .orange
limitLine.valueTextColor = .orange
limitLine.lineDashLengths = [3.0]
chartView.leftAxis.addLimitLine(limitLine)
}
Output: -
Requirement: -
please refer above images,
For example, the limit line here is starting from the highest bar to the lowest bar but I would like to show this limit line only for the average bar. Is there a way?

Change position of data labels to bottom of the line in ios charts

I have used this library on swift called: iOS Charts https://github.com/danielgindi/ios-charts
I have two datasets and I want to set the position of data labels in one of the dataset to the bottom of the line, so the numbers can be visible and no overlapping happens. How can I do this?
I setup the chart as follows:
private func configureChart() {
lineChartView = LineChartView(frame: CGRect(x: 0, y: 60, width: self.view.frame.width, height: 200))
lineChartView?.delegate = self
lineChartView?.chartDescription?.enabled = false
lineChartView?.dragEnabled = true
lineChartView?.setScaleEnabled(false)
lineChartView?.pinchZoomEnabled = false
lineChartView?.rightAxis.enabled = false
lineChartView?.xAxis.valueFormatter = self
lineChartView?.xAxis.granularity = 1
lineChartView?.legend.form = .line
lineChartView?.animate(yAxisDuration: 0.3)
if let lineChartView = lineChartView {
dashboardHeaderView?.subviews.filter({ $0 is LineChartView }).forEach {
$0.removeFromSuperview()
}
dashboardHeaderView?.addSubview(lineChartView)
}
setupLineChartData()
}
func setupLineChartData() {
monthData = ReportModel.monthlyOveralInfo()
let costSet = self.provideLineData(type: .totalCost)
let incomeSet = self.provideLineData(type: .totalIncome)
let lineChartData = LineChartData(dataSets: [incomeSet, costSet])
lineChartView?.data = lineChartData
lineChartView?.setVisibleXRangeMaximum(5)
lineChartView?.moveViewToX(lineChartView?.chartXMax ?? 0)
}
private func provideLineData(type: SWMonthlyOverallType) -> LineChartDataSet {
var mainColor: UIColor = .black
var gradientFirstColor: UIColor = .clear
var gradientSecondColor: UIColor = .black
if type == .totalIncome {
mainColor = .myAppGreen
gradientFirstColor = .clear
gradientSecondColor = .myAppGreen
}
let totalCosts = monthData.compactMap({
$0.items.first(where: {$0.type == type})
})
var index: Double = -1
let values: [ChartDataEntry] = totalCosts.compactMap({
index += 1
return ChartDataEntry(x: index, y: $0.value)
})
let chartDataSet = LineChartDataSet(values: values, label: type.rawValue)
chartDataSet.resetColors()
chartDataSet.drawIconsEnabled = false
chartDataSet.setColor(mainColor)
chartDataSet.setCircleColor(mainColor)
chartDataSet.lineWidth = 1
chartDataSet.circleRadius = 3
chartDataSet.drawCircleHoleEnabled = true
chartDataSet.valueFont = .systemFont(ofSize: 9)
let gradientColors = [gradientFirstColor.cgColor,
gradientSecondColor.cgColor]
let gradient = CGGradient(colorsSpace: nil, colors: gradientColors as CFArray, locations: nil)
chartDataSet.fillAlpha = 0.5
if let gradient = gradient {
chartDataSet.fill = Fill(linearGradient: gradient, angle: 90)
}
chartDataSet.drawFilledEnabled = true
return chartDataSet
}
Just posting in case someone finds it to be useful:
chart.xAxis.labelPosition = .bottom

Only one label appearing on a BarChart created with Charts

I want to create a BarChart with the Charts library. Everything is working good except the labels on the x axis. Only the first label "Jan" is appearing on the line. This is my code
override func viewWillAppear(_ animated: Bool) {
doBarChart()
}
func doBarChart(){
barChartView.drawBarShadowEnabled = false
barChartView.drawValueAboveBarEnabled = true
barChartView.chartDescription?.enabled = false
barChartView.maxVisibleCount = 60
let xAxis = barChartView.xAxis
xAxis.axisLineColor = UIColor.black
xAxis.labelPosition = .bottom
xAxis.drawAxisLineEnabled = true
xAxis.drawGridLinesEnabled = false
xAxis.granularity = 1.0
xAxis.labelCount = 1
// xAxis.setLabelCount(7, force: true)
let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",]
xAxis.valueFormatter = IndexAxisValueFormatter(values:months)
//Also, you probably want to add:
let leftAxis = barChartView.leftAxis;
leftAxis.enabled = false
leftAxis.drawAxisLineEnabled = false;
leftAxis.drawGridLinesEnabled = false;
leftAxis.axisMinimum = 0.0; // this replaces startAtZero = YES
let rightAxis = barChartView.rightAxis
rightAxis.enabled = false;
rightAxis.drawAxisLineEnabled = true;
rightAxis.drawGridLinesEnabled = false;
rightAxis.axisMinimum = 0.0; // this replaces startAtZero = YES
let l = barChartView.legend
l.enabled = false
barChartView.fitBars = true;
barChartView.animate(xAxisDuration: 0.2, yAxisDuration: 1.0, easingOptionX: .easeInExpo, easingOptionY: .easeInExpo)
setDataCount(count: 7, range: 50)
}
func setDataCount(count: Int, range: Double){
let barWidth = 7.0
let spaceForBar = 10.0
var yVals = [BarChartDataEntry]()
yVals.append(BarChartDataEntry(x: Double(0) * spaceForBar, y: 44.5))
yVals.append(BarChartDataEntry(x: Double(1) * spaceForBar, y: 78.1))
yVals.append(BarChartDataEntry(x: Double(2) * spaceForBar, y: 50.3))
yVals.append(BarChartDataEntry(x: Double(3) * spaceForBar, y: 56.6))
yVals.append(BarChartDataEntry(x: Double(4) * spaceForBar, y: 20.5))
yVals.append(BarChartDataEntry(x: Double(5) * spaceForBar, y: 44.3))
yVals.append(BarChartDataEntry(x: Double(6) * spaceForBar, y: 54.4))
var set1 : BarChartDataSet!
if let count = barChartView.data?.dataSetCount, count > 0{
set1 = barChartView.data?.dataSets[0] as! BarChartDataSet
set1.values = yVals
set1.colors = [UIColor.black,UIColor.orange,UIColor.red,UIColor.green,UIColor.yellow,UIColor.blue,UIColor.gray]
barChartView.data?.notifyDataChanged()
barChartView.notifyDataSetChanged()
}else{
set1 = BarChartDataSet(values: yVals, label: "DataSet")
set1.colors = [UIColor.black,UIColor.orange,UIColor.red,UIColor.green,UIColor.yellow,UIColor.blue,UIColor.gray]
var dataSets = [BarChartDataSet]()
dataSets.append(set1)
let data = BarChartData(dataSets: dataSets)
data.barWidth = barWidth;
barChartView.data = data
}
}
Add the following delegate method of "IAxisValueFormatter"
And assign these delegate.
class xyz : IAxisValueFormatter{
weak var axisFormatDelegate: IAxisValueFormatter?
//add these line in the func...
func doBarChart{
let xAxisValue = lineChartView.xAxis
xAxisValue.valueFormatter = axisFormatDelegate
lineChartView.xAxis.granularityEnabled = true
lineChartView.xAxis.granularity = 1.0
}
//add these method..
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return yvlaues[Int(value) % yvlaues.count]
}
}

iOS CorePlot point conversion

I am experimenting with Core Plot. I am trying to add a custom "goal" label over the graph at x: 2.0, y: 50.0 - basically label over the y == 50, its in a separate view, which means I need to convert my point from Core Plot to my UIView bounds. I have not found a combination of points conversions from layer/views that works across all iPhones screen sizes. In my pictures below iPhone 6s is the closest.
iPhone 6s+:
iPhone 6s:
iPhone 5s:
My view layout:
Here is my class:
class BarChartViewController: UIViewController, CPTBarPlotDataSource
{
private var barGraph : CPTXYGraph? = nil
#IBOutlet weak var textBox: UILabel!
#IBOutlet weak var graphHostingView: CPTGraphHostingView!
#IBOutlet weak var textBoxView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(false, animated: false)
self.title = "My results"
let newGraph = CPTXYGraph(frame: CGRectZero)
let theme = CPTTheme(named: kCPTPlainWhiteTheme)
newGraph.applyTheme(theme)
let hostingView = graphHostingView
hostingView.hostedGraph = newGraph
if let frameLayer = newGraph.plotAreaFrame
{
// Border
frameLayer.borderLineStyle = nil
frameLayer.cornerRadius = 0.0
frameLayer.masksToBorder = false
// Paddings
newGraph.paddingLeft = 0.0
newGraph.paddingRight = 0.0
newGraph.paddingTop = 0.0
newGraph.paddingBottom = 0.0
frameLayer.paddingLeft = 70.0
frameLayer.paddingTop = 20.0
frameLayer.paddingRight = 20.0
frameLayer.paddingBottom = 80.0
}
// Plot space
let plotSpace = newGraph.defaultPlotSpace as? CPTXYPlotSpace
plotSpace?.yRange = CPTPlotRange(location: 0.0, length: 100.0)
plotSpace?.xRange = CPTPlotRange(location: 0.0, length: 4.0)
let axisSet = newGraph.axisSet as? CPTXYAxisSet
if let x = axisSet?.xAxis {
x.axisLineStyle = nil
x.majorTickLineStyle = nil
x.minorTickLineStyle = nil
x.majorIntervalLength = 5.0
x.orthogonalPosition = 0.0
x.title = "X Axis"
x.titleLocation = 7.5
x.titleOffset = 55.0
// Custom labels
x.labelRotation = CGFloat(M_PI_4)
x.labelingPolicy = .None
let customTickLocations = [0.5, 1.5, 2.5]
let xAxisLabels = ["Label A", "Label B", "Label C"]
var labelLocation = 0
var customLabels = Set<CPTAxisLabel>()
for tickLocation in customTickLocations
{
let newLabel = CPTAxisLabel(text: xAxisLabels[labelLocation], textStyle: x.labelTextStyle)
labelLocation += 1
newLabel.tickLocation = tickLocation
newLabel.offset = x.labelOffset + x.majorTickLength
newLabel.rotation = 0 //CGFloat(M_PI_4)
customLabels.insert(newLabel)
}
x.axisLabels = customLabels
}
if let y = axisSet?.yAxis
{
y.axisLineStyle = nil
y.majorGridLineStyle = CPTMutableLineStyle()
var style = y.majorTickLineStyle?.mutableCopy() as? CPTMutableLineStyle
//style!.lineColor = CPTColor(CGColor: UIColor.blackColor()) //UIColor.blackColor())
style!.lineWidth = 10.0
y.minorGridLineStyle = CPTMutableLineStyle()
style = y.minorTickLineStyle?.mutableCopy() as? CPTMutableLineStyle
//style.lineColor = UIColor.redColor()
style!.lineWidth = 10.0
style!.lineCap = .Round
y.majorTickLength = 10.0
y.majorIntervalLength = 50.0
y.orthogonalPosition = 0.0
y.title = "Y Axis"
y.titleOffset = 45.0
y.titleLocation = 150.0
y.labelRotation = 0
y.labelingPolicy = .None
let customTickLocations = [50, 100]
let yAxisLabels = ["50", "100"]
var labelLocation = 0
var customLabels = Set<CPTAxisLabel>()
for tickLocation in customTickLocations
{
let newLabel = CPTAxisLabel(text: yAxisLabels[labelLocation], textStyle: y.labelTextStyle)
labelLocation += 1
newLabel.tickLocation = tickLocation
newLabel.offset = y.labelOffset + y.majorTickLength
newLabel.rotation = 0 //CGFloat(M_PI_4)
customLabels.insert(newLabel)
}
y.axisLabels = customLabels
var nums = Set<NSNumber>()
nums.insert(NSNumber(double: 50.0))
y.majorTickLocations = nums
}
// First bar plot
let barPlot1 = CPTBarPlot.tubularBarPlotWithColor(CPTColor.yellowColor(), horizontalBars: false)
barPlot1.baseValue = 0.0
barPlot1.dataSource = self
//barPlot1.barOffset = 0.5
barPlot1.identifier = "Bar Plot 1"
let textStyle = CPTMutableTextStyle()
textStyle.color = CPTColor.redColor()
textStyle.fontSize = 10.0
barPlot1.labelTextStyle = textStyle
newGraph.addPlot(barPlot1, toPlotSpace: plotSpace)
self.barGraph = newGraph
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.view.layoutIfNeeded()
let point: [Double] = [2.0, 50.0]
let plotPoint = UnsafeMutablePointer<Double>(point)
var dataPoint = self.barGraph?.defaultPlotSpace?.plotAreaViewPointForDoublePrecisionPlotPoint(plotPoint, numberOfCoordinates: 2)
//dataPoint = graphHostingView.layer.convertPoint(dataPoint!, fromLayer: self.barGraph!.plotAreaFrame!.plotArea)
dataPoint = graphHostingView.layer.convertPoint(dataPoint!, toLayer: graphHostingView.layer.superlayer)
//dataPoint = barGraph?.convertPoint(dataPoint!, fromLayer: graphHostingView.layer)
dataPoint = self.textBoxView.convertPoint(dataPoint!, fromView: graphHostingView)
print(dataPoint!)
for item in (self.textBoxView?.constraints)!
{
if let id = item.identifier
{
if id == "goalX"
{
print(item.constant)
item.constant = CGFloat((dataPoint?.x)!) - item.constant
}
else if id == "goalY"
{
print(item.constant)
item.constant = CGFloat((dataPoint?.y)!) - item.constant
}
}
}
barGraph?.titleDisplacement = CGPoint(x: dataPoint!.x * -1, y: dataPoint!.y * -1)
barGraph?.titlePlotAreaFrameAnchor = .TopLeft
self.textBoxView?.layoutIfNeeded()
}
}
Based on some other questions I have seen about CorePlot I know I need to convert it from CorePlot layer to my new view. I left some of my experiments in viewWillAppear() to see what I have tried. None of the solutions on SO seemed to work and I am new to iOS so probably missing something. Any ideas of what conversions I need to do to get my label to show up properly across all screen sizes?
You're on the right track with the point conversion. This is how I would do it:
// Update layer layout if needed
self.view.layoutIfNeeded()
graphHostingView.layer.layoutIfNeeded()
// Point in data coordinates
let point: [Double] = [2.0, 50.0]
// Data point in plot area layer coordinates
var dataPoint = self.barGraph?.defaultPlotSpace?.plotAreaViewPointForPlotPoint(point)
// Convert data point to graph hosting view coordinates
dataPoint = graphHostingView.layer.convertPoint(dataPoint!, fromLayer: self.barGraph!.plotAreaFrame!.plotArea)
Use this to update the constraints on the text box relative to the graph hosting view.

Attributes of text above specific bar with ios-charts

How can I change attributes (e.g. font size, text color, etc...) of text above a specific bar in a BarChart?
In this example, I want "-$5,000.00" in red and to increase the font size of every text above bars.
Here's some code:
#IBOutlet weak var barChartView: BarChartView!
// init barChartView --------------------------------------
barChartView.descriptionText = ""
barChartView.legend.enabled = false
// grid lines
barChartView.xAxis.drawAxisLineEnabled = false
barChartView.xAxis.drawGridLinesEnabled = false
barChartView.leftAxis.drawAxisLineEnabled = false
barChartView.leftAxis.drawGridLinesEnabled = false
barChartView.rightAxis.drawAxisLineEnabled = false
barChartView.rightAxis.drawGridLinesEnabled = false
// X-axis line
barChartView.xAxis.drawAxisLineEnabled = true
barChartView.xAxis.axisLineColor = axisGridsAndLabelsColor
// X-axis labels
barChartView.xAxis.labelTextColor = axisGridsAndLabelsColor
barChartView.xAxis.labelPosition = .Bottom
// Y-axis labels
accountsBarChartView.leftAxis.labelTextColor = axisGridsAndLabelsColor
accountsBarChartView.rightAxis.drawLabelsEnabled = false
//---------------------------------------------------------
// bar chart's data
var dataPoints = [String]()
var values = [Double]()
var colors = [UIColor]()
// build bar chart's data...
// dataEntries and barChartDataSet
var dataEntries = [ChartDataEntry]()
for i in 0..<dataPoints.count
{
let dataEntry = BarChartDataEntry(value: values[i], xIndex: i)
dataEntries.append(dataEntry)
}
let barChartDataSet = BarChartDataSet(yVals: dataEntries, label: "")
barChartDataSet.colors = colors
// valueFormatter
let currencyNumberFormatter = NSNumberFormatter()
currencyNumberFormatter.numberStyle = .CurrencyStyle
currencyNumberFormatter.minimumFractionDigits = 2
currencyNumberFormatter.maximumFractionDigits = 2
barChartDataSet.valueFormatter = currencyNumberFormatter
// barChartData
let barChartData = BarChartData(xVals: dataPoints, dataSet: barChartDataSet)
barChartView.data = barChartData
To set your own colors/font you can use properties valueColors and 'valueFont' of BarChartDataSet class
So it'll be something like this
...
var valueColors = [UIColor]()
// dataEntries and barChartDataSet
var dataEntries = [ChartDataEntry]()
for i in 0..<dataPoints.count
{
let dataEntry = BarChartDataEntry(value: values[i], xIndex: i)
dataEntries.append(dataEntry)
if values[i] < 0 {
valueColors.append(UIColor.redColor())
}
else {
valueColors.append(UIColor.greenColor())
}
}
let barChartDataSet = BarChartDataSet(yVals: dataEntries, label: "")
barChartDataSet.colors = colors
barChartDataSet.valueColors = valueColors
barChartDataSet.valueFont = *font you want*
If you want to change the text attributes of labels below bars you can use:
barChartView.xAxis.labelFont = UIFont.systemFont(ofSize: 5)
barChartView.xAxis.labelTextColor = UIColor.red

Resources