Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pie Chart Legends Missing with the dynamically filled data. #518

Closed
ghost opened this issue Oct 29, 2015 · 20 comments
Closed

Pie Chart Legends Missing with the dynamically filled data. #518

ghost opened this issue Oct 29, 2015 · 20 comments

Comments

@ghost
Copy link

ghost commented Oct 29, 2015

I am trying to implement pie chart from ios-charts library everything is working great, however the legends are missing from the graph. The dataPoints and values are supplied dynamically through a for loop
image

 for od in chartArray {
        let date = od["Date"] as! NSDate
        let month = formatter.stringFromDate(date)
        if self.dateDict.indexForKey(month) != nil {
            self.dateDict[month]! += 1.0
        }else{
            self.dateDict.updateValue(1.0, forKey: month)
        }
    }


    let dataPointArray = Array(dateDict.keys)
    let valuesArray = Array(dateDict.values)
    setChart(dataPointArray, values: valuesArray)

I have also logged this issue in SO Pie Chart Legends Missing

Request Help for same.

Thanks & Regards

@liuxuan30
Copy link
Member

Do you have the data code I can reproduce it in the ChartsDemo?
I don't have a swift pie chart to try yet. BTW, can you try the latest release?

@liuxuan30
Copy link
Member

Basically I am guessting the problem should be in public func renderLegend or computeLegend when you 'dynamicly' set the data.

Each time when you call pieChart.data = yourData, it will invoke notifyDataSetChanged and compute the legends and then redraw the pie chart, so the code looks totally fine to me, and should work no matter 'static' or 'dymaic' in your case

    public override func notifyDataSetChanged()
    {
        if (_dataNotSet)
        {
            return
        }

        calcMinMax()

        if (_legend !== nil)
        {
            _legendRenderer.computeLegend(_data)
        }

        calculateOffsets()

        setNeedsDisplay()
    }

I am not sure what's your exact procedure for 'static' and 'dynamic' way to set the data, you can try dynamicly add the value and see if each time the pie chart is redrawed and renderLegend is called and have your latest legends. You are welcome to debug it.

@ghost
Copy link
Author

ghost commented Nov 3, 2015

Thank you for the response. What do you mean by the data code please let me know I will provide.I am using the latest release, I couldn't try the charts demo since it is in objective C and I work with swift only.
I will try to debug it and will update the result.

@liuxuan30
Copy link
Member

OK, could you zip a sample project with the pie chart bug?

@simon4ever
Copy link

Same problem for me. After filling the chart initially with data, every works fine (first screenshot). However, after clicking one of the segmented controls, which filters the data, the legend gets messed up. The interesting thing is that the values and the amount of categories displayed in the chart is correct. It's just not correct in the legend part.

By the way: printing the values for legend and data values gives me correct results. Is there a way of resetting the legend I might be missing? Thanks for the support!

bildschirmfoto 2015-11-11 um 19 40 23
bildschirmfoto 2015-11-11 um 19 40 39

@ghost
Copy link
Author

ghost commented Nov 12, 2015

Hi simon4ever,

I tried to replicate it with the static data and it works fine for me, however whenever I use the dynamic code I get the same problem again and again. So I tried to debug it as much as i could and found that exact same code works fine when I generate the data in application and feed it to chart but when I get the data from parse I get this problem. Here is a Working Demo of the pie chart with static data. I will soon post the non working example too.

@pmairoldi
Copy link
Collaborator

I might be wrong but this sounds like an issue of doing UI operations not on the main thread. That would be what I would check first since you said t works fine when the app generates the data but not when it comes from parse. I'm assuming you are fetching the data from parse not on the main thread.

@ghost
Copy link
Author

ghost commented Nov 12, 2015

That might be the case but can you please elaborate what you mean by not fetching the data on main thread. Here is what I am doing -

1 - Have a class datamodel in that model I am fetching data from parse asynchronously and on successful completion of fetch I am invoking a delegate method.
2 - The delegate method then informs chartview to take data from the datamodel and set the chart.
3 - Chart gets loaded with complete data but without the legends

You can check the code for this part in stack overflow here .Here is the data which I am printing just before setting the chart in real app-

     Date Dict: ["Sep": 6.0, "Aug": 3.0, "Mar": 6.0, "Dec": 6.0, "May": 3.0, "Apr": 3.0, "Oct": 6.0, "Jan":                       12.0, "Jul": 3.0, "Nov": 3.0]
    Keys: LazyMapCollection<Dictionary<String, Double>, String>(_base: ["Sep": 6.0, "Aug": 3.0, "Mar":   6.0, "Dec": 6.0, "May": 3.0, "Apr": 3.0, "Oct": 6.0, "Jan": 12.0, "Jul": 3.0, "Nov": 3.0], _transform:   (Function))
    Values LazyMapCollection<Dictionary<String, Double>, Double>(_base: ["Sep": 6.0, "Aug": 3.0, "Mar": 6.0, "Dec": 6.0, "May": 3.0, "Apr": 3.0, "Oct": 6.0, "Jan": 12.0, "Jul": 3.0, "Nov": 3.0], _transform: (Function))
    Data Point Array ["Sep", "Aug", "Mar", "Dec", "May", "Apr", "Oct", "Jan", "Jul", "Nov"]
   Values Array [6.0, 3.0, 6.0, 6.0, 3.0, 3.0, 6.0, 12.0, 3.0, 3.0]

Here is what I am doing in demo app -

  1. Create a date range in viewDidLoad iterate through the date range to generate similar Data Point Array and Values array. The only difference is I am not doing it on delegate method this is in viewDidLoad

  2. Setting the chart data in viewDidLoad, here is the data I am printing before setting the chart in demo app -

    Date Dict: ["Feb": 20.0, "Dec": 31.0, "Jan": 31.0, "Nov": 18.0]
    Keys: LazyMapCollection<Dictionary<String, Double>, String>(_base: ["Feb": 20.0, "Dec": 31.0, "Jan":  31.0, "Nov": 18.0], _transform: (Function))
     Values LazyMapCollection<Dictionary<String, Double>, Double>(_base: ["Feb": 20.0, "Dec": 31.0,  "Jan": 31.0, "Nov": 18.0], _transform: (Function))
     Data Point Array ["Feb", "Dec", "Jan", "Nov"]
     Values Array [20.0, 31.0, 31.0, 18.0]
    

You can check the second part here

@simon4ever
Copy link

Okay, my approach is really similar to AppsWise's way:

  1. I generate the data and assign it to the pie chart in viewDidLoad. This is the static part, as it always works the same way.
  2. As soon as the user presses one of the "All", "Top 3", "Top 10" Buttons, @IBAction func TopAllSegmentedControlPressed(sender: AnyObject) is called and the same functions as in viewDidLoad are called, just that parameters are set a little differently to filter the results according to the user selection.

Now, I assumed that both of these operations run in the same thread. But I may be wrong, because I am not an iOS expert for sure.

@pmairoldi
Copy link
Collaborator

can you please elaborate what you mean by not fetching the data on main thread

Well when doing asynchronous work usually to set it to be done in the background so when it finishes you need to switch back to the main thread if you want to do actions on the UI. You want to do so since all UI actions HAVE to happen on the main thread. That is maybe why you are seeing some undefined behaviour. If this is the case you need to do something like this:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {

    let textData = doNetworkRequest()

    dispatch_async(dispatch_get_main_queue()) {
        label.text = textData
    }
}

@ghost
Copy link
Author

ghost commented Nov 13, 2015

Hi Peterster,

Thanks for putting thoughts around it, I tried dispatch_async(dispatch_get_mainqueue()) at various places, it doesn't seems to solve the problem here is what i tried.

       func setChart(dataPoints: [String], values: [Double]) {
        dispatch_async(dispatch_get_main_queue()) {
        var chartDataEntries: [ChartDataEntry] = []
        for i in 0..<dataPoints.count {

            let chartDataEntry = ChartDataEntry(value: values[i], xIndex: i)
            chartDataEntries.append(chartDataEntry)
        }

        let pieChartDataSet = PieChartDataSet(yVals: chartDataEntries, label: "Months")
        let pieChartData = PieChartData(xVals: dataPoints, dataSet: pieChartDataSet)
        self.pieChartView.data = pieChartData
        self.pieChartView.animate(xAxisDuration: NSTimeInterval(5))


        var colors: [UIColor] = []

        for _ in 0..<dataPoints.count {
            let red = Double(arc4random_uniform(256))
            let green = Double(arc4random_uniform(256))
            let blue = Double(arc4random_uniform(256))

            let color = UIColor(red: CGFloat(red/255), green: CGFloat(green/255), blue: CGFloat(blue/255), alpha: 1)
            colors.append(color)


            pieChartDataSet.colors = colors
        }
      }
  }

Executing the whole set chart logic in main queue. But the result was same.
Then I tried calling setChart method in main queue as -

     dispatch_async(dispatch_get_main_queue()) {
    self.setChart(dataPointArray, values: valuesArray)
    }

This also doesn't work. I also tried executing whole delegate method in main queue in data model as -

      dispatch_async(dispatch_get_main_queue()) {
                    self.delegate?.dataDidLoad(self)
                }

But this also did not work. Can you please help to find if i miss anything or doing something wrong here ? For a change to debug it more I will try to put bar chart or some other chart and see if that also miss the label or legends. Will update soon.

@pmairoldi
Copy link
Collaborator

I don't know. Looks correct. Could you create a sample project that does this and then send it to us so that we can check if it's a bug. Thanks.

@ghost
Copy link
Author

ghost commented Nov 15, 2015

@petester42 @liuxuan30
I have created a sample project to replicate this. Please find it here Please help to find & debug the problem. The credentials for app is

User Name - Demo
Password - Demo

Thanks & Regards

@pmairoldi
Copy link
Collaborator

I've found the problem. You are doing operations on the chart after setting the data. This seems to cause issues. If I change the setCharts function to set the data after changing the colors it works fine. Best practice would be to create all your datasets first and only set the data after all the data is created.

    func setChart(dataPoints: [String], values: [Double]) {

        var chartDataEntries: [ChartDataEntry] = []
        for i in 0..<dataPoints.count {

            let chartDataEntry = ChartDataEntry(value: values[i], xIndex: i)
            chartDataEntries.append(chartDataEntry)
        }

        let pieChartDataSet = PieChartDataSet(yVals: chartDataEntries, label: "Months")

        // Don't set data here
        // let pieChartData = PieChartData(xVals: dataPoints, dataSet: pieChartDataSet)
        // self.pieChartView.data = pieChartData
        // self.pieChartView.animate(xAxisDuration: NSTimeInterval(5))

        var colors: [UIColor] = []

        for _ in 0..<dataPoints.count {
            let red = Double(arc4random_uniform(256))
            let green = Double(arc4random_uniform(256))
            let blue = Double(arc4random_uniform(256))

            let color = UIColor(red: CGFloat(red/255), green: CGFloat(green/255), blue: CGFloat(blue/255), alpha: 1)
            colors.append(color)


            pieChartDataSet.colors = colors
        }

        // Set data here
        let pieChartData = PieChartData(xVals: dataPoints, dataSet: pieChartDataSet)
        self.pieChartView.data = pieChartData
        self.pieChartView.animate(xAxisDuration: NSTimeInterval(5))
    }

Seems like not setting the colors on the dataset before the data will not show the legend properly. I don't know if this could be considered a bug. What do you think @danielgindi.

@simon4ever
Copy link

Thanks, moving the part where the data is set to the end of my setPieChart function solved the issue! 👍

@ghost
Copy link
Author

ghost commented Nov 15, 2015

Wonderful works perfectly fine. Thank you very much to all of you.

@liuxuan30
Copy link
Member

I have met some issues also related to the order; I always call setData after everything is settled down. Also wondering could this be improved. But we indeed needs to calculate some values that's related to the chart setting.

@appstute-prasad
Copy link

In Swift 3

let data: PieChartData = PieChartData(xVals: xVals, dataSet: dataSet)

is not available, it giving error cannot invoke method.
How to resolve this problem

@liuxuan30
Copy link
Member

use Chart 3.0 APIs and refer ChartsDemo.

@aakashjaiswal15
Copy link

While creating data entries for Pie Chart use

let dataEntry = PieChartDataEntry(value: Double(values[i]), label: dataPoints[i])

instead of

let chartDataEntry = ChartDataEntry(value: values[i], xIndex: i)

also change the array type to var dataEntries: [PieChartDataEntry] = []

Doing this, you will get all the legends corresponding to every data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants