Charts: Version 3.0.1: Fill gradient fills out the wrong side of the chart

Created on 16 Dec 2016  路  6Comments  路  Source: danielgindi/Charts

Where the fill gradient generally works as expected I noticed a single instance where it filled out the wrong side of the LineChartView. Unfortunately I do not have any steps to reproduce, but it would appear there is some bug with the fill logic.

screen shot 2016-12-16 at 13 09 09

The specific LineChartDataSet fill gradient logic involved:

        let set = LineChartDataSet(values: dataEntries, label: "")

        ...

        // add fill gradient
        let locations: [CGFloat] = [ 0.0, 1.0 ]
        let colors = [UIColor.white.cgColor, UIColor.cyan.cgColor] as CFArray
        let colorspace = CGColorSpaceCreateDeviceRGB()

        guard let gradient = CGGradient(colorsSpace: colorspace, colors: colors, locations: locations) else {
            return set
        }

        // fill with gradient
        set.fillAlpha = 1
        set.fill = Fill(linearGradient: gradient, angle: 90)
        set.drawFilledEnabled = true

        return set

Most helpful comment

@liuxuan30 : thank you for your answer, using a custom fill formatter solves the issue I am having 馃憤

To document to whoever runs into this particular issue again: I have added my own fill formatter.

Update to the dataset:

        // create dataset
        let set = LineChartDataSet(values: dataEntries, label: "")
        ...

        // use custom fill formatter
        set.fillFormatter = MyFillFormatter()

And the fill formatter:

class MyFillFormatter: IFillFormatter {
    func getFillLinePosition(dataSet: ILineChartDataSet, dataProvider: LineChartDataProvider) -> CGFloat {
        guard let yMin = dataProvider.lineData?.yMin, let yMax = dataProvider.lineData?.yMax else {
            return 0
        }

        let yRange = yMax - yMin
        let spaceBottomFactor = dataProvider.getAxis(YAxis.AxisDependency.right).spaceBottom
        let yBottom = yMin - (yRange * Double(spaceBottomFactor))

        return CGFloat(yBottom)
    }
}

All 6 comments

If you could provide use the values of dataset that would make it easier to find the issue. Thanks!

Looking again at this specific issue I am noticing that most of the values are negative while the last part consists of positive values. It seems like the fill gradient is always applied to the surface between the DataSet's line and the 0-value instead of always _below_ the DataSet line (what I would expect to happen).

pasted_image_16_12_16_16_35

Is this by design?

I can consistently reproduce by negating the values, consider the following screenshot. Left is the a chart, and on the right is the exact same chart but then with the values negated.

pasted_image_16_12_16_16_52

If I remember correctly, The filling feature is around zero line. So it fill the rect among zero line and other lines. So it works as expected?
If you really need the opposite effect, use positive values and format it with a '-' prefix using valueFormatter, and combine other tricks, just tweaking the labels it display.
e.g. [0, 800] as [-800,0] by a custom formatter.

Another way is to supply your own getFillLinePosition():

@objc(IChartFillFormatter)
public protocol IFillFormatter
{
    /// - returns: The vertical (y-axis) position where the filled-line of the LineDataSet should end.
    func getFillLinePosition(dataSet: ILineChartDataSet, dataProvider: LineChartDataProvider) -> CGFloat
}

and take a look at drawLinearFill(), generateFilledPath()

@liuxuan30 : thank you for your answer, using a custom fill formatter solves the issue I am having 馃憤

To document to whoever runs into this particular issue again: I have added my own fill formatter.

Update to the dataset:

        // create dataset
        let set = LineChartDataSet(values: dataEntries, label: "")
        ...

        // use custom fill formatter
        set.fillFormatter = MyFillFormatter()

And the fill formatter:

class MyFillFormatter: IFillFormatter {
    func getFillLinePosition(dataSet: ILineChartDataSet, dataProvider: LineChartDataProvider) -> CGFloat {
        guard let yMin = dataProvider.lineData?.yMin, let yMax = dataProvider.lineData?.yMax else {
            return 0
        }

        let yRange = yMax - yMin
        let spaceBottomFactor = dataProvider.getAxis(YAxis.AxisDependency.right).spaceBottom
        let yBottom = yMin - (yRange * Double(spaceBottomFactor))

        return CGFloat(yBottom)
    }
}

I was able to make the fill always draw below the line by initializing my values to MinY and using native MyFillFormatter.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

BrandonShega picture BrandonShega  路  4Comments

anhltse03448 picture anhltse03448  路  3Comments

coop44483 picture coop44483  路  3Comments

guanyanlin picture guanyanlin  路  3Comments

deepumukundan picture deepumukundan  路  3Comments