From the release notes of Charts 3.0.1:
Added a new IndexAxisValueFormatter that you can pass an array of labels and have them behave like in Charts 2.0
I tried to use IndexAxisValueFormatter to achieve the same result as i had with Charts v2. But it behaves quite different.
In Charts 2 i used
LineChartData(xVals: ["125", "250", "500", "1k", "2k", "4k"], dataSets: dataSets)
to achieve the following result:

The labels on the x-axis were always aligned to the value dots in the graph. Even when i zoomed in.
Now, to achieve the same with Charts 3.0.1 i tried
chart.xAxis.valueFormatter = IndexAxisValueFormatter(values: ["125", "250", "500", "1k", "2k", "4k"])
but the result was

As you can see, the labels appear ...somewhere. When you zoom, it sometimes fits. But mostly not.
My temporarily solution is to force the label count and lock scrolling, like this:
let labels = ["125", "250", "500", "1k", "2k", "4k"]
chart.xAxis.valueFormatter = IndexAxisValueFormatter(values: labels)
chart.xAxis.setLabelCount(labels.count, force: true)
chart.scaleXEnabled = false
...because zooming would break it again. But this way it limits the user experience.
So my questions are:
IndexAxisValueFormatter?Also curious how IndexAxisValueFormatter is supposed to function. Currently half of my xAxis labels are not appearing even with chart.xAxis.setLabelCount(labels.count, force: true)
I need @danielgindi to answer your Q1
for the x axis issue, You can see, in your screenshot with Chart 2.x, [125,250,500,1k,2k] are equal distance to each other, which is NOT correct in terms of real world axis: 125 and 250 has 125 distance, and 250, 500 has 250 distance. They should not be equal width.
In chart 2.x, due to the fact it's index based, so basically it's equal width. However in Chart 3.0, x axis is more like y axis, so you see your second pic, the x axis labels reflect the true distance to each other. However it seems weird again, 125 - 250 has longer distance then 500-1k, 1k-2k.. I am not sure why so right now.
I think for your needs, you can just have x axis values to be 1,2,3,4,5 to make it equal distance, and then format it as your old 125,250,500,1k,2k?
Hey @liuxuan30,
thanks for your answer! Actually i am already using index values for the x-axis. This is what i basically do:
let yValues: [Double] = [0.12, 0.06, 0.058, 0.09, 0.12, 0.14]
let entries = yValues.enumerated().map { (i, val) in
return ChartDataEntry(x: Double(i), y: val)
}
So if i麓m getting it right, i already do what you propose.
If x vals are already indexed numbers, I think it should be fine:
x vals are 0,1,2,3,4,5, and you have equal instance on x axis direction.
If you then set the labelCount and enable forcelabelEnabled, it should be fine.
What's your problem now? Can you print out the xAxis.entries to make sure it's [0,1,2,3,4,5]?
Also, checking the formatter:
open func stringForValue(_ value: Double,
axis: AxisBase?) -> String
{
let index = Int(value.rounded())
if index < 0 || index >= _valueCount || index != Int(value)
{
return ""
}
return _values[index]
}
You also need to check let index = Int(value.rounded()) not causing any offset.
My problem is chart.scaleXEnabled = false.
My current solution works only because i disable the scrolling. But that麓s a hack to me. And it麓s not what the(my) user was used to.
In Charts 2, the labels kept alignment to the data points even if i zoomed in.
ok.. have you checked when you zoom, what are the xAxis.entries?
Hmm.. I don麓t know if this is the info you want, but i captured the zoom behavior with and without chart.xAxis.setLabelCount(labels.count, force: true).
Zoom without fixed label count:

Zoom with fixed label count:

As you see, both are not quite nice. I麓ll try to capture the Charts 2 behavior on an old device when i麓m back home. Just to make clear what i expected to see.
__Update:__ My other device is not running the old version anymore. So i can麓t show you right now.
@liuxuan30
I have the same issues when adding labels using ChartIndexAxisValueFormatter, and I don't even have to have zoom enabled. You need to stop telling people to just look at computeAxisValues() and label this as a bug.
Seems a bug to me now..
@r-dent, My first thought is that because x axis is recalculating the entries, so it generate new values, and when it's rounded to int, it fetches dup labels and render it around.
I don麓t know enough about the inner functioning of Charts to make a guess here, sorry. 鈽猴笍
found this in the source code
// If granularity is enabled, then do not allow the interval to go below specified granularity.
// This is used to avoid repeated values when rounding values for display.
if axis.granularityEnabled
{
interval = interval < axis.granularity ? axis.granularity : interval
}
If you set granularityEnabled to true you may prevent duplicate labels since your indices are fractions. Probably won't fix disappearing labels though, still trying to figure that out.
I played a bit around with granularity. Set it to 0.1.. Enabled it.. Disabled it.. So far, nothing helped.
As I said, computesAxisValues() calculates xAxis.entries, which are the values to format.
I don't have time to look into this.. you are welcome to try 馃憤
I ended up with this exact same issue. The way I solved it was to set granularity to 1.0 for the xAxis. The issue was that we ended up with fractions on the x axis, thus grabbing the same entry twice and sometimes ending up with no entry. It's more obvious when you have very few entries so I forced it by having 2 values in a bar chart and this went haywire.
Again, set the granularity of the axis you want an array with strings to 1.0 and it cleared it all up for me.
Just want to share my experience, I solved this issue using a dynamic granularity, make chart adjust while zooming
Important do not force labelCount
First I have many datasets, so I group datasets values using
let groupSpace = 0.4
let barSpace = 0.03
let barWidth = 0.2
data.barWidth = barWidth
chart.xAxis.axisMinimum = 0.0
chart.xAxis.axisMaximum = 0.0 + data.groupWidth(groupSpace: groupSpace, barSpace: barSpace) * Double(labels.count)
data.groupBars(fromX: 0.0, groupSpace: groupSpace, barSpace: barSpace)
Adjust granularity base on how many columns you want to see
chart.xAxis.granularity = chart.xAxis.axisMaximum / Double(labels.count)
Then set data
chart.data = data
Then I use a custom Formatter to calculate label to be shown
class CustomLabelsAxisValueFormatter : NSObject, 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 ""
}
}
Set the custom formatter
let formatter = CustomLabelsAxisValueFormatter()
formatter.labels = xVals
chart.xAxis.valueFormatter = formatter
Try and let me know. Happy coding!
@samueleperricone Your solution works. I am able to zoom as well. Thank you
It works.Thanks a lot.....i have added
graphPresentingView.xAxis.setLabelCount(labels.count, force: false) for the correct alignment of x-axis
Most helpful comment
Just want to share my experience, I solved this issue using a dynamic granularity, make chart adjust while zooming
Important do not force labelCount
First I have many datasets, so I group datasets values using
Adjust granularity base on how many columns you want to see
chart.xAxis.granularity = chart.xAxis.axisMaximum / Double(labels.count)Then set data
chart.data = dataThen I use a custom Formatter to calculate label to be shown
Set the custom formatter
Try and let me know. Happy coding!