Charts: LineChart X-Axis - How To Always Display First & Last Label

Created on 26 Dec 2016  ·  21Comments  ·  Source: danielgindi/Charts

In my Line Chart, I have it set up so that if there's only one data entry, I make the data point show up in the middle like this:

screen shot 2016-12-25 at 7 00 36 pm

If there's more data entries, it'll look more like this:

screen shot 2016-12-25 at 7 00 50 pm

The problem I'm having is that the last label on the X-Axis is not showing up. This is data from the past 30 days, so I'd like the last label, Dec 25, to show up. Is there a way to make my Line Chart always display the first and last label on the X-Axis? I don't care how it computes or skips the labels in between.

bug

Most helpful comment

    lineChart?.xAxis.setLabelCount(arrOfMonth.count, force: true)

This will help for display last value of line chart - i tried and worked for me

All 21 comments

try avoidFirstLastClippingEnabled, default is false.

    /// if set to true, the chart will avoid that the first and last label entry in the chart "clip" off the edge of the chart
    open var avoidFirstLastClippingEnabled = false

@cnowak7 Were you able to solve the issue mentioned in the 2nd image? Please share the solution

avoidFirstLastClippingEnabled does not help. The last label always gets cut

If I look into the source code, the axis entries are calculated in AxisRendererBase.computeAxisValues and there is no way to enforce the max value to be displayed. I believe this functionality was supported in the previous version though.

https://github.com/danielgindi/Charts/issues/2007 seems same issue with this one?
@ondrejhanslik by default, computeAxisValues() will take min and max value to calculate the range. So the min and max value should be larger than the data's min/max value to hold all the data. Have you checked what's your min/max value in computeAxisValues(), and what's your data's min/max value?

@liuxuan30 I use custom String array as X-Axis Values i.e. Dates

Here's a screenshot of my chart

untitled

This is how my chart looks when setting avoidFirstLastClippingEnabled to true:

screen shot 2016-12-28 at 11 02 15 pm

Here's some extra code that I'm using to set up my X-Axis:

private func setUpLineChartPreferences() {
        .
        .
        .
        // x-axis
        self.lineChart.xAxis.labelPosition = .bottom
        self.lineChart.xAxis.drawGridLinesEnabled = true
        self.lineChart.xAxis.granularity = 1.0
        .
        .
        .
}
private func setUpLineChart(withDataEntries entries: [ChartDataEntry]) {
        let entryCount = entries.count
        if entryCount > 0 {
            let dataSet = LineChartDataSet(values: entries, label: nil)
            dataSet.drawFilledEnabled = true
            dataSet.fillColor = .green
            dataSet.circleRadius = 5
            dataSet.drawValuesEnabled = false
            dataSet.setColor(.black)
            dataSet.highlightColor = .green
            dataSet.setCircleColor(.blue)
            let lineChartData = LineChartData(dataSet: dataSet)
            self.lineChart.data = lineChartData
            // SET UP X-AXIS
            self.lineChart.xAxis.valueFormatter = IndexAxisValueFormatter(values: self.chartLabels)
            self.lineChart.xAxis.avoidFirstLastClippingEnabled = true
            print("NOWAK: CHART LABELS THO - \(self.chartLabels)")
            if entryCount == 1 {
                self.lineChart.xAxis.axisMinimum = -1.0
                self.lineChart.xAxis.axisMaximum = 1.0
            } else {
                self.lineChart.xAxis.axisMinimum = 0.0
                self.lineChart.xAxis.axisMaximum = Double(self.chartLabels.count - 1)
            }
            print("X MIN IS \(self.lineChart.xAxis.axisMinimum) AND X MAX IS \(self.lineChart.xAxis.axisMaximum)")
        } else {
            self.lineChart.noDataText = "No Stats For The Past \(self.currentFilter.capitalized)"
            self.lineChart.setNeedsDisplay()
        }
        switch self.currentFilter {
        case "week":
            self.weekFilterButton.backgroundColor = .blue
            self.monthFilterButton.backgroundColor = .darkGray
            self.yearFilterButton.backgroundColor = .darkGray
        case "month":
            self.monthFilterButton.backgroundColor = .blue
            self.weekFilterButton.backgroundColor = .darkGray
            self.yearFilterButton.backgroundColor = .darkGray
        case "year":
            self.yearFilterButton.backgroundColor = .blue
            self.weekFilterButton.backgroundColor = .darkGray
            self.monthFilterButton.backgroundColor = .darkGray
        default:
            print("ERROR SETTING FILTER BUTTON BACKGROUND COLORS")
        }
        self.stopLoading()
}

I am using Charts 3.0.1 by the way :)

Same here. Charts 3.0.1, XCode 8.0 and Swift 3.0

If a solution isn't possible for this situation, I guess a workaround that would probably make sense would be to, when the chart is displayed, highlight the last value (today) so that the "BalloonMarker" shows up displaying the text "Dec XX\n500".

On my side obviously :)

OK I checked it.

In chart 3.0, x axis behaves like y axis, so you can check out your y axis labels, the biggest y axis label is smaller than data's max value, in
image

In computeAxisValues(), it reads min and max value from the axis min/max value( which is the data's min/max value) and calculates an interval to get the x axis labels.

For example in ChartsDemo - Line Chart Dual YAxis, if my x values are from 0 to 20,
computeAxisValues() takes 0, 20 as min/max value, then, my labelCount is 6 by default, it will calculates rawInterval = (max - min) / labelCount, and later the final interval will be 3. Then it starts from 0, to get 7(labelCount + 1) labels, which are 0,3,6,9,12,15,18. The x axis labels last one is 18, not 20.

The reason isAvoidFirstLastClippingEnabled does not work is because it handles when the last label is out of bound, so it's irrelevant to this issue.

If you want to sure the label that is your data's x value, you have to tweak computeAxisValues() to return the labels. Just check out computeAxisValues() to see the details and you should be able to change it.

@danielgindi what you do think for such user cases?

@liuxuan30 We don't want to tweak computeAxisValues to return some specific labels. For example, when I am displaying a chart over dates (e.g. x-values are days of a week, days of a month or months of a year), I need the first and last labels to be displayed (e.g. Monday and Sunday or 1/1 and 31/1).

I first generate the formatted values (e.g. day names or formatted days) and then set them as:

    xAxis.axisMinimum = 0
    xAxis.axisMaximum = Double(xValues.count - 1)
    xAxis.valueFormatter = IndexAxisValueFormatter(values: xValues)

What I need is a smarter computeAxisValues that will always display the first label and the last label and then will calculate the labels between them.

This is a very basic and very common use case.

I understand. There are still bugs about the x axis labels, e.g. label overlap.
Current logic is first knowing the min/max value, and generate intervals from it and accumulate to get the axis labels. However current computeAxisValues() does not guarantee the max value / last label is xAxis.axisMaximum. That's what I need @danielgindi's input about this.

Maybe I should use 'override' rather than 'tweak' for computeAxisValues(). You can provide an implementation to make sure your first/last value is always displayed as workaround. However, this might cause the label out of bound or other issues.

Another way is, you can try setLabelCount(count, forceLabelEnabled) equals your x values count or half to see the result.
The logic is quite diffrent then:

        // 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
        }

It does not involves the addtional process, just simple math.

@liuxuan30 What if the x-axis values are in String format? In my case, the x-axis values are Dates in String format

@vaibhav-varshaaweblabs x axis now is taking double ONLY in Chart 3.x, so you can use valueFormatter to display the label instead of the double. However, the valueFormatter might be complicated, when you need to consider zooming/scrolling

Has anyone found a solution for this in Charts 3.0.2? I'm using date strings for my x-axis labels and seeing the last label disappear occasionally, but having trouble reproducing. This is with isAvoidFirstLastClippingEnabled set to true. I'm wondering if this could be related to #2563?

@vaibhav-varshaaweblabs @raudabaugh : How did u guys manage to plot strings on the X-Axis, as the setDataCount method is expecting int or double values for X and Y.
Kindly let me know, thanks in advance

bottomAxis.setLabelCount(entries.size(), true); and also set margin right to make visible last count.

    lineChart?.xAxis.setLabelCount(arrOfMonth.count, force: true)

This will help for display last value of line chart - i tried and worked for me

Was this page helpful?
0 / 5 - 0 ratings

Related issues

newbiebie picture newbiebie  ·  3Comments

cilasgimenez picture cilasgimenez  ·  4Comments

valeIT picture valeIT  ·  3Comments

Aungbandlab picture Aungbandlab  ·  4Comments

guoyutaog picture guoyutaog  ·  3Comments