how to create Bar Charts and change the number to string values? - ios

I have create the Bar Charts in ViewController using SwiftCharts framework, and i get the graph and bar charts below,
graph X Values are showed numbers but i need to change the string value (Example: 1 changed to BMW, 3 changed to Audi, 4 changed to Bens, 6 changed to Maruthi) how, and my code is given below,
import UIKit
import SwiftCharts
class BarViewController: UIViewController {
private var chart: Chart?
let sideSelectorHeight: CGFloat = 50
private func barsChart(horizontal horizontal: Bool) -> Chart {
let tuplesXY = [(1, 8), (3, 2), (4, 4), (6, 6)]
func reverseTuples(tuples: [(Int, Int)]) -> [(Int, Int)] {
return tuples.map{($0.1, $0.0)}
}
let chartPoints = (horizontal ? reverseTuples(tuplesXY) : tuplesXY).map{ChartPoint(x: ChartAxisValueInt($0.0), y: ChartAxisValueInt($0.1))}
let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont)
let (axisValues1, axisValues2) = (
0.stride(through: 10, by: 2).map {ChartAxisValueDouble(Double($0), labelSettings: labelSettings)},
0.stride(through: 7, by: 1).map {ChartAxisValueDouble(Double($0), labelSettings: labelSettings)}
)
let (xValues, yValues) = horizontal ? (axisValues1, axisValues2) : (axisValues2, axisValues1)
let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: "Product Details", settings: labelSettings))
let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: "Sales Percentage", settings: labelSettings.defaultVertical()))
let barViewGenerator = {(chartPointModel: ChartPointLayerModel, layer: ChartPointsViewsLayer, chart: Chart) -> UIView? in
let bottomLeft = CGPointMake(layer.innerFrame.origin.x, layer.innerFrame.origin.y + layer.innerFrame.height)
let barWidth: CGFloat = Env.iPad ? 60 : 30
let (p1, p2): (CGPoint, CGPoint) = {
if horizontal {
return (CGPointMake(bottomLeft.x, chartPointModel.screenLoc.y), CGPointMake(chartPointModel.screenLoc.x, chartPointModel.screenLoc.y))
} else {
return (CGPointMake(chartPointModel.screenLoc.x, bottomLeft.y), CGPointMake(chartPointModel.screenLoc.x, chartPointModel.screenLoc.y))
}
}()
return ChartPointViewBar(p1: p1, p2: p2, width: barWidth, bgColor: UIColor.blueColor().colorWithAlphaComponent(0.6))
}
let frame = ExamplesDefaults.chartFrame(self.view.bounds)
let chartFrame = self.chart?.frame ?? CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height - sideSelectorHeight)
let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: ExamplesDefaults.chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel)
let (xAxis, yAxis, innerFrame) = (coordsSpace.xAxis, coordsSpace.yAxis, coordsSpace.chartInnerFrame)
let chartPointsLayer = ChartPointsViewsLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, chartPoints: chartPoints, viewGenerator: barViewGenerator)
let settings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.blackColor(), linesWidth: ExamplesDefaults.guidelinesWidth)
let guidelinesLayer = ChartGuideLinesDottedLayer(xAxis: xAxis, yAxis: yAxis, innerFrame: innerFrame, settings: settings)
return Chart(
frame: chartFrame,
layers: [
xAxis,
yAxis,
guidelinesLayer,
chartPointsLayer]
)
}
private func showChart(horizontal horizontal: Bool) {
self.chart?.clearView()
let chart = self.barsChart(horizontal: horizontal)
self.view.addSubview(chart.view)
self.chart = chart
}
override func viewDidLoad() {
super.viewDidLoad()
self.showChart(horizontal: false)
}
Thanks in Advance

I have not worked with this library but I think the data which is inputted as tuple can be changed. Like this
("BMW":8), ("AUDI": 4)
The type of a tuple is determined by the values it has.
Hope it helps.

Related

How to add custom value in charts for bottom label in swift iOS

I have to show [0, 25, 50, 75, 100] with dollar sign in Charts using swift iOS
var xValue = [25.0, 50.0]
var dollarValue = ["$25", "$50", "$75", "$100"]
Added in view didLoad
rightAxis.valueFormatter = IndexAxisValueFormatter(values: dollarValue)
func updateCharts() {
var chartDataEntry = [BarChartDataEntry]()
let spaceForBar = 10.0;
for i in 0..<xValue.count {
let value = BarChartDataEntry(x: Double(i) * spaceForBar, y: xValue[i])
chartDataEntry.append(value)
}
let chartDataSet = BarChartDataSet(entries: chartDataEntry, label: "10x In-Store ")
chartDataSet.colors = ChartColorTemplates.material()
let chartMain = BarChartData(dataSet: chartDataSet)
chartMain.barWidth = 5.0
horizontalBarChartContainnerView.animate(yAxisDuration: 0.5)
horizontalBarChartContainnerView.data = chartMain
}
I wanted to show 0 $25 $50 $75 $100 instead of 0 10 20 30 40 50
You need to create a custom formatter
public class CustomAxisValueFormatter: NSObject, AxisValueFormatter {
public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return "$\(Int(value))"
} }
and set the axis value formatter to your custom formatter
xAxis.valueFormatter = CustomAxisValueFormatter()
This code is working fine
public class CustomAxisValueFormatter: AxisValueFormatter {
var labels: [String] = []
public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let count = self.labels.count
guard let axis = axis, count > 0 else {
return ""
}
let factor = axis.axisMaximum / Double(count)
let index = Int((value / factor).rounded())
if index >= 0 && index < count {
return self.labels[index]
}
return ""
}
}
Add inside of viewDidLoad()
rightAxis.axisMaximum = 4
let customAxisValueFormatter = CustomAxisValueFormatter()
customAxisValueFormatter.labels = ["$25", "$80", "$75", "$100"]

How can I fix error multiple colors of BarChart in Swift?

I have a bar chart to show item using Chart library. I got an error that the explain description show multiple colors for each item. Please help me how can i fix this, i want to show exactly the color like column (only one color), not multiple like this. Thanks for your helping.
My Screen
Here is my code, note that items is dynamic, i got from API:
func setDataCount(items: [Item]) {
let dataEntry = (0..<items.count).map { (i) -> ChartDataEntry in
let value = Double(items[i].soldQuantity)
return BarChartDataEntry(x: Double(i), y: value)
}
var arrayDataSet = [IChartDataSet]()
var arrayColor = [UIColor]()
var set = BarChartDataSet()
for i in 0...items.count - 1 {
set = BarChartDataSet(entries: dataEntry, label: items[i].name)
arrayColor.append(UIColor.random)
set.colors = arrayColor
// CONVERT DOUBLE -> INT
set.valueFormatter = DefaultValueFormatter(decimals: 0)
arrayDataSet.append(set)
}
let data = BarChartData(dataSets: arrayDataSet)
data.setValueFont(.systemFont(ofSize: 10, weight: .bold))
data.barWidth = 0.2
chartView.data = data
}

Swiftchars - bars chart overlap after specific number of points

How can I specify min space between bars in bar chart using SwiftChars library ?
Here is my code sample for using SwiftCharts to display bars chart:
// xAxis
let labelSettings = ChartLabelSettings(font: .systemFont(ofSize: 13.0), fontColor: .aiduBlue)
var xValues = [ChartAxisValue]()
xValues.append(ChartAxisValueString(order: -1))
for (index, point) in barsDataSource.enumerated() {
let dateString = DateFormatter.shortDayOfWeekDayOnTwoLinesFormatter.string(from: point.departureDate)
xValues.append(ChartAxisValueString(dateString, order: index, labelSettings: labelSettings))
}
xValues.append(ChartAxisValueString(order: xValues.count - 1))
let xModel = ChartAxisModel(axisValues: xValues,
lineColor: .aiduSkyBlue,
axisTitleLabel: ChartAxisLabel(text: "", settings: labelSettings))
// yAxis
let yAxisMinSpaceBetweenPoints: CGFloat = 10.0
let yAxisTopBottomMargin = (((barsMaxPrice - barsMinPrice) / Double(barsDataSource.count)) + Double(yAxisMinSpaceBetweenPoints)) + 10
let yAxisGenerator = ChartAxisValuesGeneratorNice(minValue: barsMinPrice - yAxisTopBottomMargin,
maxValue: barsMaxPrice + yAxisTopBottomMargin,
preferredDividers: 1,
minSpace: yAxisMinSpaceBetweenPoints,
maxTextSize: CGFloat.greatestFiniteMagnitude,
multiplierUpdateMode: .nice)
let yLabelsGenerator = ChartAxisLabelsGeneratorFunc {scalar in
return ChartAxisLabel(text: "", settings: ChartLabelSettings())
}
let yModel = ChartAxisModel(lineColor: UIColor.white.withAlphaComponent(0),
firstModelValue: barsMinPrice,
lastModelValue: barsMaxPrice,
axisTitleLabels: [],
axisValuesGenerator: yAxisGenerator,
labelsGenerator: yLabelsGenerator)
// Char Bars layer
let frame = chartFrame(containerView.bounds)
let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartSettings,
chartFrame: frame,
xModel: xModel,
yModel: yModel)
let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame)
let barsModels: [ChartBarModel] = barsDataSource.enumerated().flatMap { index, item in
[
ChartBarModel(constant: ChartAxisValueInt(index),
axisValue1: ChartAxisValueDouble(0),
axisValue2: ChartAxisValueDouble(item.price),
bgColor: barColor(price: item.price, minPrice: barsMinPrice))
]
}
let chartBarSettings = ChartBarViewSettings(animDuration: 0,
animDelay: 0,
cornerRadius: 0,
roundedCorners: .allCorners,
selectionViewUpdater: nil,
delayInit: false)
let chartBarsLayer = ChartBarsLayer(xAxis: xAxisLayer.axis,
yAxis: yAxisLayer.axis,
bars: barsModels,
horizontal: false,
barWidth: 40,
settings: chartBarSettings,
mode: .translate,
tapHandler: { [weak self] (tappedBar) in
self?.chartBarTapHandler(tappedBar: tappedBar)
}, viewGenerator: { [weak self] (p1, p2, barWidth, color, settings, model, index) -> ChartPointViewBar in
var barBGColor = color
if let s = self, let lastTappedBarModel = s.lastTappedBarModel {
let currentBarModel = s.dataSource[index]
barBGColor = currentBarModel.departureDate == lastTappedBarModel.departureDate && currentBarModel.duration == lastTappedBarModel.duration ? s.barSelectionBgColor : color
}
let view = ChartPointViewBar(p1: p1, p2: p2, width: barWidth, bgColor: barBGColor, settings: settings)
return view
})
// Price labels layer
let labelToBarSpace: Double = 20
let labelChartPoints = barsModels.map { bar in
ChartPoint(x: bar.constant, y: bar.axisValue2.copy(bar.axisValue2.scalar + labelToBarSpace))
}
let priceLabelsLayer = ChartPointsViewsLayer(xAxis: xAxisLayer.axis,
yAxis: yAxisLayer.axis,
chartPoints: labelChartPoints,
viewGenerator: {(chartPointModel, layer, chart) -> UIView? in
let label = HandlingLabel()
label.text = PriceFormatter.string(fromPrice: Float(chartPointModel.chartPoint.y.scalar - labelToBarSpace))
label.font = .boldSystemFont(ofSize: 10.0)
label.textColor = UIColor.aiduBlue
label.sizeToFit()
let pos = chartPointModel.chartPoint.y.scalar > 0
label.center = CGPoint(x: chartPointModel.screenLoc.x, y: pos ? innerFrame.origin.y : innerFrame.origin.y + innerFrame.size.height)
label.alpha = 0
label.isUserInteractionEnabled = false
label.movedToSuperViewHandler = {[weak label] in
label?.alpha = 1
label?.center.y = chartPointModel.screenLoc.y
}
return label
}, displayDelay: 0, mode: .translate)
return Chart(
frame: frame,
innerFrame: innerFrame,
settings: chartSettings,
layers: [
xAxisLayer,
yAxisLayer,
chartBarsLayer,
priceLabelsLayer
]
)
private var chartSettings: ChartSettings {
var chartSettings = ChartSettings()
chartSettings.leading = 2
chartSettings.top = 2
chartSettings.trailing = 2
chartSettings.bottom = 2
chartSettings.axisStrokeWidth = 0.4
chartSettings.spacingBetweenAxesX = 2
chartSettings.spacingBetweenAxesY = 2
chartSettings.labelsSpacing = 10
chartSettings.labelsToAxisSpacingY = 0
chartSettings.spacingBetweenAxesY = 0
chartSettings.axisTitleLabelsToLabelsSpacing = 0
chartSettings.zoomPan.panEnabled = true
chartSettings.zoomPan.zoomEnabled = false
chartSettings.zoomPan.maxZoomX = 3
chartSettings.zoomPan.minZoomX = 3
chartSettings.zoomPan.minZoomY = 1
chartSettings.zoomPan.maxZoomY = 1
return chartSettings
}
When data source contains around (10 for ex) points, chart working without overlapping, but without fixed space between bars if you changed (increase/decrease) data source:
When data source contains around (35 for ex) points, bars overlapped:
SwiftCharts version 0.6.5
Swift version 5
Finally I found solution:
I had to set the minZoomX and maxZoomX (in chart settings) with fixed value which the number of pages for the chart (or number of scrollable area than the original width).
why x ? because I want chart to scroll horizontally.
here is code:
let barWidth: CGFloat = 30.0
let spaceBetweenBars: CGFloat = 80.0
let zoomXValue = CGFloat(barsDataSource.count) / (UIScreen.main.bounds.width / (barWidth + spaceBetweenBars))
chartSettings.zoomPan.minZoomX = zoomXValue
chartSettings.zoomPan.maxZoomX = zoomXValue

iOS Charts not showing all xAxis Value

I am writing an iOS App in Swift 4.2
I am using Charts library to display Horizontal Bar Chart. I need to set its xAxis Values. Only last value is being displayed.
Issue Screenshot:
Code Snippet:
#IBOutlet var chartView: HorizontalBarChartView!
override func viewDidLoad() {
super.viewDidLoad()
let xAxis = chartView.xAxis
xAxis.labelPosition = .bottom
xAxis.labelFont = .systemFont(ofSize: 10)
xAxis.drawAxisLineEnabled = false
xAxis.drawGridLinesEnabled = false
xAxis.granularity = 1
xAxis.enabled=true
xAxis.setLabelCount(3, force: false) //Not working as per expectations
xAxis.valueFormatter=IndexAxisValueFormatter(values: ["A","B","C"]) //Not working as per expectations
self.setDataCount(3,values:[142000,122400,100110])
chartView.animate(yAxisDuration: 2.5)
}
func setDataCount(_ count: Int, values:[Double]) {
let barWidth = 2.0
let spaceForBar = 4.0
var n=0
let yVals = (0..<count).map { (i) -> BarChartDataEntry in
let val = values[n]
n+=1
return BarChartDataEntry(x: Double(i)*spaceForBar, y: val)
}
let set1 = BarChartDataSet(entries: yVals, label: "DataSet")
set1.drawIconsEnabled = false
let data = BarChartData(dataSet: set1)
data.setValueFont(UIFont(name:"HelveticaNeue-Light", size:10)!)
data.barWidth = barWidth
chartView.data = data
//let xAxis = chartView.xAxis
//xAxis.setLabelCount(3, force: false)
//xAxis.valueFormatter=IndexAxisValueFormatter(values: ["A","B","C"])
}
I tried to find solutions:
xAxis.valueFormatter=IndexAxisValueFormatter(values: ["A","B","C"])
but these are not working.
This is happening because the default functionality of IndexAxisValueFormatter
You have to code your own custom IAxisValueFormatter like this
final class CustomFormatter: IAxisValueFormatter {
var labels: [String] = []
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let count = self.labels.count
guard let axis = axis, count > 0 else {
return ""
}
let factor = axis.axisMaximum / Double(count)
let index = Int((value / factor).rounded())
if index >= 0 && index < count {
return self.labels[index]
}
return ""
}
}
Then use it like
let customFormater = CustomFormater()
customFormater.labels = ["A","B","C"]
chartView.xAxis.valueFormatter = customFormater
Make sure the size/count of the data and labels is the same or it wont give you the desired results.
I updated your given code please check changes.
I hope its work for you.
class ViewController: UIViewController {
#IBOutlet var chartView: HorizontalBarChartView!
override func viewDidLoad() {
super.viewDidLoad()
let xAxis = chartView.xAxis
xAxis.labelPosition = .bottom
xAxis.labelFont = .systemFont(ofSize: 10)
xAxis.drawAxisLineEnabled = false
xAxis.drawGridLinesEnabled = false
xAxis.granularity = 1
xAxis.enabled=true
xAxis.setLabelCount(3, force: false) //Not working as per expectations
xAxis.valueFormatter = IndexAxisValueFormatter(values: ["A","B","C"])//AxisValueFormatter(values: ["A","B","C"]) //Not working as per expectations
self.setDataCount(3,values:[142000,122400,100110])
chartView.animate(yAxisDuration: 2.5)
}
func setDataCount(_ count: Int, values:[Double]) {
let yVals = (0..<count).map { (i) -> BarChartDataEntry in
let val = values[i]
return BarChartDataEntry(x: Double(i), y: val)
}
let set1 = BarChartDataSet(values: yVals, label: "DataSet")
set1.drawIconsEnabled = false
let data = BarChartData(dataSet: set1)
data.setValueFont(UIFont(name:"HelveticaNeue-Light", size:10)!)
//data.barWidth = barWidth
chartView.data = data
//let xAxis = chartView.xAxis
//xAxis.setLabelCount(3, force: false)
//xAxis.valueFormatter=IndexAxisValueFormatter(values: ["A","B","C"])
}
}

How to add string labels( xAxis labels) to horizontal bar in Charts framework

I am using charts framework for drawing charts. I need to add some strings in left of every bars. In my code always there are two bars, one for Income and one for Expense. I want to show these string aside of every bar.
Below you can see my codes:
let ys1 = [model.totalIncome, abs(model.totalExpense)]
var yse1 = ys1.enumerated().map { x, y -> BarChartDataEntry in
return BarChartDataEntry(x: Double(x), y: y)
}
let count = 2
let data = BarChartData()
let ds1 = BarChartDataSet(values: yse1, label: "")
ds1.colors = [UIColor.red, UIColor.blue]
// Number formatting of Values
ds1.valueFormatter = YAxisValueFormatter()
ds1.valueFont = UIFont(fontType: .H6)!
ds1.valueTextColor = UIColor.dark_text
// Data set
data.addDataSet(ds1)
horizontalBarChartView.data = data
// General setting
horizontalBarChartView.xAxis.drawLabelsEnabled = false
horizontalBarChartView.setScaleEnabled(false)
// Grid
horizontalBarChartView.xAxis.drawGridLinesEnabled = false
horizontalBarChartView.leftAxis.gridLineDashLengths = [15.0, 15.0]
horizontalBarChartView.leftAxis.gridColor = UIColor.palette_28
horizontalBarChartView.rightAxis.drawGridLinesEnabled = false
// Disable number formatting of leftAxis and rightAxis
horizontalBarChartView.leftAxis.enabled = false
horizontalBarChartView.rightAxis.enabled = false
horizontalBarChartView.xAxis.valueFormatter =
IndexAxisValueFormatter(values: ["Income","Expense"])
horizontalBarChartView.xAxis.granularityEnabled = true
horizontalBarChartView.xAxis.granularity = 1
// setViewPortOffsets
let digitCount = Int(data.yMax).digitCount
let leftOffcet: CGFloat = digitCount > 2 ? CGFloat((digitCount - 1) * 10) : 10.0
horizontalBarChartView.setViewPortOffsets(left: leftOffcet, top: 0, right: 0, bottom: 50)
horizontalBarChartView.leftAxis.axisMinimum = 0
And my view:
lazy var horizontalBarChartView: HorizontalBarChartView = {
let chartView = HorizontalBarChartView()
chartView.contentMode = .scaleAspectFit
chartView.drawBordersEnabled = false
chartView.drawGridBackgroundEnabled = false
chartView.gridBackgroundColor = UIColor.clear
chartView.legend.enabled = false
chartView.chartDescription = nil
chartView.highlighter = nil
chartView.clipsToBounds = true
chartView.translatesAutoresizingMaskIntoConstraints = false
return chartView
}()
I guess below code should add these labels, however, it is not working
horizontalBarChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: ["Income","Expense"])
horizontalBarChartView.xAxis.granularityEnabled = true
horizontalBarChartView.xAxis.granularity = 1
Update
See orange rectangle. I need these labels.
I am using an extension for this
extension BarChartView {
private class BarChartFormatter: NSObject, IAxisValueFormatter {
var labels: [String] = []
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return labels[Int(value)]
}
init(labels: [String]) {
super.init()
self.labels = labels
}
}
func setBarChartData(xValues: [String], yValues: [Double], label: String) {
var dataEntries: [BarChartDataEntry] = []
for i in 0..<yValues.count {
let dataEntry = BarChartDataEntry(x: Double(i), y: yValues[i])
dataEntries.append(dataEntry)
}
let chartDataSet = BarChartDataSet(values: dataEntries, label: label)
let chartData = BarChartData(dataSet: chartDataSet)
let chartFormatter = BarChartFormatter(labels: xValues)
let xAxis = XAxis()
xAxis.valueFormatter = chartFormatter
self.xAxis.valueFormatter = xAxis.valueFormatter
self.data = chartData
}
}
you can use this in code like this
chartView.setBarChartData(xValues: xEntries, yValues: yEntries, label: "Income")
where,
//You need to add values dynamically
var yEntries: [Double] = [1000, 200, 500]
var xEntries: [String] = ["Income1", "Income2", "Income3"]
Hope this works.
Charts framework is a great tool for drawing charts. You should study it perfectly. I used an old code in our project (copy/paste), so it caused some problems for drawing xAxis labels in my chart.
Just comment below line:
horizontalBarChartView.xAxis.drawLabelsEnabled = false
When you create your BarChartDataSet you can pass the label.
let data = BarChartData()
let ds1 = BarChartDataSet(values: yse1, label: "Income")
ds1.colors = [UIColor.red, UIColor.blue]
Hope this helps.
self.ChartObj.xAxis.valueFormatter = DefaultAxisValueFormatter(block: {(index, _) in
print(index)
return YourArray.object(at: Int(index)) as! String
})
self.ChartObj.xAxis.setLabelCount(YourArray.count, force: true)
Try this code

Resources