Charts: Full control over x labels in 3.0.0

Created on 8 Nov 2016  路  2Comments  路  Source: danielgindi/Charts

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?

Most helpful comment

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;

All 2 comments

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;
Was this page helpful?
0 / 5 - 0 ratings

Related issues

anhltse03448 picture anhltse03448  路  3Comments

PrashantKT picture PrashantKT  路  3Comments

guanyanlin picture guanyanlin  路  3Comments

valeIT picture valeIT  路  3Comments

deepumukundan picture deepumukundan  路  3Comments