I have been performing migration from 2.1.6 version of iOS charts library to 3.0.0.
And after APIs changed I am no longer able to set labels on exact positions on my chart.
Example: Month can have from 29 to 31 days. The task is to have labels for each 7 days: |1 |8 |15 |22 |29. The issue is that if I use
[xAxis setLabelCount:5 force:YES];
I will get different values depending on number of days in month in delegate method of IChartAxisValueFormatter protocol.
#pragma IChartAxisValueFormatter
- (NSString * _Nonnull)stringForValue:(double)value axis:(ChartAxisBase * _Nullable)axis
In 2.1.6 version of library the delegate method was called for each x and now only lablesCount amount of times with automatically computed values.
Is there any way to have full control over x labels in 3.0.0? Or I am doing something wrong?
in 3.0, x axis behaves like y axis, not indexed based axis anymore. So you have to clean up your mind and think in the new way.
3.0 makes the x axis more flexible, so I think you can take full control over it.
The x axis labels range are calculated by computeAxisValues(), so you can override it.
For you question,
// force label count
if axis.isForceLabelsEnabled
{
interval = Double(range) / Double(labelCount - 1)
// Ensure stops contains at least n elements.
axis.entries.removeAll(keepingCapacity: true)
axis.entries.reserveCapacity(labelCount)
var v = yMin
for _ in 0 ..< labelCount
{
axis.entries.append(v)
v += interval
}
n = labelCount
}
Is how labelCount works. You can override this part to have interval = 7.
Thank you @liuxuan30. My final solution is:
import UIKit
import Charts
import Foundation
import CoreGraphics
@objc class CustomXAxisRenderer: XAxisRenderer {
public var labelInterval: Double = 0.0
open override func computeAxisValues(min: Double, max: Double)
{
let interval = self.labelInterval
if(interval==0.0) {
super.computeAxisValues(min: min, max: max)
} else {
guard let axis = self.axis else { return }
let yMin = min
let yMax = max
let range = abs(yMax - yMin)
let labelCount = Int(floor(range/interval))+1
if labelCount == 0 || range <= 0 || range.isInfinite
{
axis.entries = [Double]()
axis.centeredEntries = [Double]()
return
}
var n = axis.centerAxisLabelsEnabled ? 1 : 0
// Ensure stops contains at least n elements.
axis.entries.removeAll(keepingCapacity: true)
axis.entries.reserveCapacity(labelCount)
var v = yMin
for _ in 0 ..< labelCount
{
axis.entries.append(v)
v += interval
}
n = labelCount
// set decimals
if interval < 1
{
axis.decimals = Int(ceil(-log10(interval)))
}
else
{
axis.decimals = 0
}
if axis.centerAxisLabelsEnabled
{
axis.centeredEntries.reserveCapacity(n)
axis.centeredEntries.removeAll()
let offset: Double = interval / 2.0
for i in 0 ..< n
{
axis.centeredEntries.append(axis.entries[i] + offset)
}
}
}
computeSize()
}
}
Use the new renderer:
self.chartView.xAxisRenderer = [[CustomXAxisRenderer alloc] initWithViewPortHandler:self.chartView.viewPortHandler
xAxis:self.chartView.xAxis
transformer:[self.chartView getTransformerForAxis:AxisDependencyLeft]];
Set label interval:
CustomXAxisRenderer *xAxisRenderer = (CustomXAxisRenderer *)self.chartView.xAxisRenderer;
xAxisRenderer.labelInterval = 7;
Most helpful comment
Thank you @liuxuan30. My final solution is:
Use the new renderer:
Set label interval: