I have an issue creating a string value for xAxis using the iOS Charts library
x value always has duplicate value, see following picture, you can see value always be JAN JAN JAN JAN FEB FEB FEB
How I can setup chart's x value as it will show JAN FEB MAR ?
import UIKit
import Charts
class ViewController: UIViewController {
var months:[String]!
#IBOutlet var lineChartView: LineChartView!
override func viewDidLoad() {
super.viewDidLoad()
let unitsSold = [20.0, 4.0, 6.0]
var months = ["Jan", "Feb", "Mar"]
let formato:LineChartFormatter = LineChartFormatter(labels: months)
let xaxis:XAxis = XAxis()
var dataEntries: [ChartDataEntry] = []
for i in 0..<unitsSold.count {
let dataEntry = ChartDataEntry(x: Double(i), y: unitsSold[i])
print("double \(Double(i))")
dataEntries.append(dataEntry)
}
xaxis.valueFormatter = formato
let data = LineChartData()
let dataset = LineChartDataSet(values: dataEntries, label: "Hello")
dataset.colors = [NSUIColor.red]
data.addDataSet(dataset)
self.lineChartView.gridBackgroundColor = NSUIColor.white
self.lineChartView.xAxis.drawGridLinesEnabled = true;
self.lineChartView.xAxis.labelPosition = XAxis.LabelPosition.bottom
self.lineChartView.xAxis.centerAxisLabelsEnabled = true
self.lineChartView.chartDescription?.text = "LineChartView Example"
self.lineChartView.xAxis.valueFormatter = xaxis.valueFormatter
self.lineChartView.data = data
}
override open func viewWillAppear(_ animated: Bool) {
self.lineChartView.animate(xAxisDuration: 1.0, yAxisDuration: 1.0)
}
}
#objc(LineChartFormatter)
public class LineChartFormatter: NSObject, IAxisValueFormatter{
var labels: [String] = []
public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return labels[Int(value)]
}
init(labels: [String]) {
super.init()
self.labels = labels
}
}
First, use granularity to avoid duplicated values.
Second, check your valueFormatter not to return same string if you found their values are very different. e.g. int(1.5) and int(1.9) will give you same Jan but you may want to let 1.9 return Feb
Again, you have to carefully implement your own valueFormatter if you are handling decimals
add this two lines in your code
lineChartView.xAxis.granularityEnabled = true
lineChartView.xAxis.granularity = 1.0
Related
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"])
}
}
I'm fairly new to using the iOS Charts library and I was able to make a bar chart, but I'm having trouble understanding how to "stack" a set of data containing the same x-axis value.
Here's what my bar chart currently looks like:
And the code to achieve this:
extension MyViewController: IAxisValueFormatter
{
func stringForValue(_ value: Double, axis: AxisBase?) -> String
{
let record_ARRAY = record_ARRAY.sorted(by: {$0.date < $1.date})
let date = record_ARRAY[Int(value)].date
return ReusableClass.typeThreeDateFormat(date: date)
}
}
var chargeDates_ARRAY = [Date]()
var chargeCosts_ARRAY = [Double]()
override func viewDidLoad()
{
chargeDates_ARRAY = [2017-08-05 07:00:00 +0000, 2017-08-06 07:00:00 +0000, 2017-08-06 07:00:00 +0000, 2017-08-07 07:00:00 +0000]
chargeCosts_ARRAY = [8.0, 3.0, 5.6600000000000001, 4.4400000000000004]
createBarChart(dataPoints: chargeDates_ARRAY, values: chargeCosts_ARRAY)
barChartView.delegate = self
}
func createBarChart(dataPoints: [Date], values: [Double])
{
var dataEntries: [BarChartDataEntry] = []
for index in 0..<dataPoints.count
{
let dataEntry = BarChartDataEntry(x: Double(index),
y: values[index])
dataEntries.append(dataEntry)
}
barChartView.xAxis.valueFormatter = self
barChartView.xAxis.granularity = 1
barChartView.xAxis.setLabelCount(dataPoints.count, force: false)
barChartView.animate(yAxisDuration: 1.0, easingOption: .easeInOutQuad)
let chartDataSet = BarChartDataSet(values: dataEntries, label: "")
let chartData = BarChartData(dataSet: chartDataSet)
barChartView.data = chartData
barChartView.zoom(scaleX: zoomXMultiplier, scaleY: 0.0, x: 0, y: 0)
barChartView.barData?.barWidth = barWidthMultiplier
}
What I like to do is combine the data containing the 2 same x-axis values 08/06/17 into one stacked bar, and achieve something like this:
I've tried to follow the answer from the question: Stacked bar chart with charts in Swift
But I couldn't get anywhere.
Can someone help? Thanks!
Try to construct your chargeCosts_ARRAY = [8.0, 3.0, 5.6600000000000001, 4.4400000000000004] as chargeCosts_ARRAY = [[8.0], [3.0, 5.6600000000000001], [4.4400000000000004]] basically you are trying to create an object of type [[Double]](). Then change BarChartDataEntry(x: Double(index), y: values[index]) to BarChartDataEntry(x: Double(index), yValues: values[index]). Happy coding :)
I added a bar chart to the storyboard, but I cannot properly set labels for my data entries.
here is my code:
var names = ["aaa", "bbb", "ccc", "ddd"]
var values = [230.0, 280.0, 450.0, 340.0]
setChart(dataPoints: names, values: values)
setChart function:
func setChart(dataPoints: [String], values: [Double])
{
let formatter = BarChartFormatter()
formatter.setValues(values: dataPoints)
let xaxis:XAxis = XAxis()
barChartView.noDataText = "You need to provide data for the chart."
var dataEntries: [BarChartDataEntry] = []
for i in 0..<dataPoints.count
{
let dataEntry = BarChartDataEntry(x: Double(i), y: values[i])
dataEntries.append(dataEntry)
}
let chartDataSet = BarChartDataSet(values: dataEntries, label: "موجودی")
let chartData = BarChartData(dataSet: chartDataSet)
xaxis.valueFormatter = formatter
barChartView.xAxis.labelPosition = .bottom
barChartView.xAxis.drawGridLinesEnabled = false
barChartView.xAxis.valueFormatter = xaxis.valueFormatter
barChartView.chartDescription?.enabled = false
barChartView.legend.enabled = true
barChartView.rightAxis.enabled = false
barChartView.data = chartData
}
and finally the formatter:
#objc(BarChartFormatter)
public class BarChartFormatter: NSObject, IAxisValueFormatter
{
var names = [String]()
public func stringForValue(_ value: Double, axis: AxisBase?) -> String
{
return names[Int(value)]
}
public func setValues(values: [String])
{
self.names = values
}
}
but it didn't work well as shown below:
as shown here, it add 6 labels instead 4 labels, and it also has duplicates.
I already read this solution, however, as you can see, it has some issues yet.
How can I solve this problem?
I think you can try to set the following properties:
barChartView.xAxis.granularityEnabled = true
barChartView.xAxis.granularity = 1.0 //default granularity is 1.0, but it is better to be explicit
barChartView.xAxis.decimals = 0
The source code tells you a lot about the properties above. https://github.com/danielgindi/Charts/blob/master/Source/Charts/Components/AxisBase.swift
I am using Daniel's IOS Chart framework and Xcode7.3.1. I went through basic tutorial and tried to set up first example.
Problem is I cannot assign X-axis string label
I see in tutorial when we assign data to chart, we should use
let chartData = BarChartData(xVals: dataPoints, dataSet: chartDataSet)
to complete last step, but below is what I am seeing here.
There is no parameter as "xVals" but only "dataset" is available.
Does anybody have idea?
The result would be just barchart without any xaxis label as below
Ok so here is example function for line chart:
func setLineChart(dataPoints: [Double], chartInfo: String, chartDescription: String) {
let formatter:ChartFormatter = ChartFormatter()
let xaxis:XAxis = XAxis()
var dataEntries: [ChartDataEntry] = Array()
for i in 0..<dataPoints.count {
let dataEntry = ChartDataEntry(x: Double(i), y: dataPoints[i])
dataEntries.append(dataEntry)
formatter.stringForValue(Double(i), axis: xaxis)
}
let lineChartDataSet = LineChartDataSet(values: dataEntries, label: chartInfo)
let lineChartData = LineChartData(dataSet: lineChartDataSet)
xaxis.valueFormatter = formatter
lineChartView.xAxis.valueFormatter = xaxis.valueFormatter
lineChartDataSet.mode = .CubicBezier
lineChartDataSet.drawCirclesEnabled = false
lineChartView.descriptionText = chartDescription
lineChartView.data = lineChartData
}
Here is formatter:
#objc(ChartFormatter)
public class ChartFormatter: NSObject, IAxisValueFormatter{
var days: [String] = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
public func stringForValue(value: Double, axis: AxisBase?) -> String{
return days[Int(value)]
}
}
And finally a call of function:
override func viewDidLoad() {
super.viewDidLoad()
let values: [Double] = [1.0, 4.0, 1.5, 1.4, 1.3, 1.0, 3.0]
let chartInfo = "chart info"
let chartDescription = "chart description"
self.setLineChart(values, chartInfo: chartInfo, chartDescription: chartDescription
}
so finally you will get something like this:
Seems like you are using old tutorials! Because the swift 3 is coming the the developers of these charts changed library .. according to them we will have to use special formatters to set for example values String of type to X axis. So now you have method which accepts only one parameter instead of two:
let barChartData = BarChartData(dataSet: chartDataSet)
let lineChartData = LineChartData(dataSet: lineChartDataSet)
More information you can find here:
https://github.com/danielgindi/Charts/issues/1340
Instead of showing the Double values in my chart I would like to show a String for each value. I still want the chart to draw from the Double values, but I want the label to show a string with a date instead. How to do this?
I´m using Daniel Cohen Gindis library.
Here is the code for my graph:
import UIKit
import Charts
class ViewController: UIViewController {
#IBOutlet var barChartView: BarChartView!
override func viewDidLoad() {
super.viewDidLoad()
//x line values
let tools = ["Saw","Sissor","Axe"
,"Hammer","Tray"]
//y line values
let values = [1.0,2.0,3.0,4.0,5.0]
setChart(tools, values: values, colors: colors)
}
func setChart(xAxisLabels: [String], values: [Double], colors: [UIColor]){
var dataEntries : [BarChartDataEntry] = []
for i in 0..<xAxisLabels.count {
let dataEntry = BarChartDataEntry(value: values[i], xIndex: i )
dataEntries.append(dataEntry)
}
barChartView.leftAxis.axisMinValue = 0.0
barChartView.leftAxis.axisMaxValue = 14.0
barChartView.data = chartData
}
}
You can use the String() method to convert any data type into a string.
For example:
var myDouble: Double = 1.5
print(String(myDouble)) // Prints "1.5"
I don't know if you still need the answer, but this may help somebody who struggles with the same problem :)
First of all, you need to make a ValueFormatter class for your xAxis.
xAxis.valueFormatter = XAxisValueFormatter()
And then you have to put the following in your new class:
class XAxisValueFormatter: NSObject, IAxisValueFormatter {
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let dateFormatterPrint = DateFormatter()
dateFormatterPrint.dateFormat = "HH:mm:ss"
let date = Date(timeIntervalSince1970: value)
let time = dateFormatterPrint.string(from: date)
return time
}
}