RDowns
RDowns

Reputation: 659

How to use iOS Charts when pulling in multiple data from an array?

I have been learning to use iOS Charts over the past few days and have successfully gotten to them work when dealing with 'single' data called in from an API.

However I am now stuck because I want to pull in multiple data from an array.

My aim is to create a Bar Chart based on the winning_streak of each user_name in the array below:

["leagueStats": {
winningStreak =     (
            {
        id = 2;
        "user_name" = Dicky;
        "winning_streak" = 5;
    },
            {
        id = 6;
        "user_name" = G;
        "winning_streak" = 2;
    },
            {
        id = 5;
        "user_name" = Sultan;
        "winning_streak" = 0;
    }
);
}
]

This is my code that retrieves the API:

This is where I need help - Do I need to produce some kind of loop? In the answer supplied the amount of users to appear in the graph is pre-programmed.

With my current code below it only picks out the first object in the array. How do I get it to cycle through the array and populate the desired variables?

   if let dict = json?["leagueStats"] as? [String:AnyObject] {

                        // WINNING STREAK

                        if let dataWinStreak = dict["winningStreak"] as? [[String : AnyObject]] {
                            let newdictWinStreak = dataWinStreak.first! // I'm guess this needs to be removed and this section re-structured?

                            let tempWinStreakNumber = newdictWinStreak ["winning_streak"] as? String
                            let tempWinStreakNewNumber = Int(tempWinStreakNumber!)
                            let doubleWinStreak = Double(tempWinStreakNewNumber!)

                            self.winStreak = doubleWinStreak
                            self.winningStreak =  ["Wins"]

                            let games = [self.winStreak]
                            self.setWinStreakChart(dataPoints: self.winningStreak, values: games)

                         }

However this code above only pulls in information from the first record in the array to use, 5.

I want to be able to produce 3 different columns, Dicky, G, Sultan, with the corresponding values of 5, 2, and 0.

This is my code for displaying the Bar Chart - currently just displaying 1 column, showing 5.

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

    let formato:WinningStreakFormatter = WinningStreakFormatter()
    let xaxis:XAxis = XAxis()

    winningStreakBarChart.noDataText = "you need to provide some data for the chart."

    var dataEntries: [BarChartDataEntry] = Array()

    for i in 0..<dataPoints.count {
        let dataEntry = BarChartDataEntry(x: Double(i), y: values[i])
        dataEntries.append(dataEntry)
        formato.stringForValue (Double(i), axis: xaxis)
    }

    xaxis.valueFormatter = formato
    winningStreakBarChart.xAxis.valueFormatter = xaxis.valueFormatter

    let chartDataSet = BarChartDataSet(values: dataEntries, label: "Games Played")
    let chartData = BarChartData(dataSets: [chartDataSet])

    chartData.addDataSet(chartDataSet)
    winningStreakBarChart.data = chartData

    winningStreakBarChart.xAxis.granularityEnabled = true
    winningStreakBarChart.xAxis.granularity = 1.0

    self.winningStreakBarChart.xAxis.labelPosition = XAxis.LabelPosition.bottom

}

This is what I have:

enter image description here

Ideally, what I want is the label 'Win Streak' to be the user_name from the API, then the legend Games _Played to say Winning Streak. In this particular instance, I want 3 columns for the 3 user_names in the array.

Once this is done, as a secondary objective I want to add another dataset for Losing Streak which would be grouped by the same user_name.

Could someone please show me how to do this? Thank you

Upvotes: 3

Views: 2298

Answers (1)

Prakash Donga
Prakash Donga

Reputation: 558

WinningStreakFormatter class is used to format xAxis labels, here I have used an approach of reusing the data from chart object as result WinningStreakFormatter have a weak reference to the chart view. So using this approach we can have data to be held by chart view rather than keeping an additional copy with WinningStreakFormatter

import Foundation
import Charts

class WinningStreakFormatter: NSObject, IAxisValueFormatter {

    weak var _chartView : BarChartView!

    init(chartView : BarChartView) {
        self._chartView = chartView
    }

    public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        let barChartData : IChartDataSet = (self._chartView.barData?.dataSets.first)!
        let barChartDataEntry : ChartDataEntry = barChartData.entryForIndex(Int(value))!
        return barChartDataEntry.data as! String
    }
}

Refer the code below in order to prepare the proposed chart, In below code chartView is basically the BarChartView type instance variable. Also, I have used the same data structure that you have in your API, you can simply replace dataPoints with your parsed data structure.

    self.chartView.drawBarShadowEnabled = false
    self.chartView.drawValueAboveBarEnabled = true
    self.chartView.chartDescription?.enabled = false

    let  xAxis : XAxis = self.chartView.xAxis;
    xAxis.labelFont = UIFont(name: "HelveticaNeue-Light", size: 10.0)! 
    xAxis.labelTextColor = UIColor.black
    xAxis.drawAxisLineEnabled = false
    xAxis.drawGridLinesEnabled = true
    xAxis.granularity = 1;
    xAxis.labelPosition = .bottom
    xAxis.valueFormatter = WinningStreakFormatter(chartView: self.chartView)

    let yAxisLeft : YAxis = self.chartView.leftAxis
    yAxisLeft.axisMinimum = 0.0

    let yAxisRight : YAxis = self.chartView.rightAxis
    yAxisRight.axisMinimum = 0.0

    let l : Legend = self.chartView.legend;
    l.horizontalAlignment = Legend.HorizontalAlignment.left
    l.verticalAlignment = Legend.VerticalAlignment.bottom
    l.orientation = Legend.Orientation.horizontal
    l.drawInside = false
    l.form = Legend.Form.square
    l.formSize = 9.0
    l.font = UIFont(name: "HelveticaNeue-Light", size: 11.0)!
    l.xEntrySpace = 4.0

    //Data Structure built for each user for winning streak, you can use your parsing logic to prepare the same
    let winningStreakDM1 : [String : Any] = [
        "id" : 2,
        "user_name" : "Dicky",
        "winning_streak" : 5
    ]

    let winningStreakDM2 : [String : Any] = [
        "id" : 6,
        "user_name" : "G",
        "winning_streak" : 2
    ]

    let winningStreakDM3 : [String : Any] = [
        "id" : 5,
        "user_name" : "Sultan",
        "winning_streak" : 0
    ]


    //Array of the each user winning streak dictionary.
    let dataPoints = [winningStreakDM1, winningStreakDM2, winningStreakDM3]

    var values = [BarChartDataEntry]()

    for i in 0..<dataPoints.count {
        let xValue = Double(i)
        let yValue = Double(dataPoints[i]["winning_streak"] as! Int)

        let barChartDataEntry = BarChartDataEntry(x: xValue, y: yValue, data: dataPoints[i]["user_name"] as AnyObject)
        values.append(barChartDataEntry)
    }

    let barChartDataSet = BarChartDataSet(values: values, label: "Winning Streak")
    barChartDataSet.colors = ChartColorTemplates.material()

    let barChartData = BarChartData(dataSet: barChartDataSet)
    barChartData.setValueFont(UIFont.systemFont(ofSize: 12.0))
    self.chartView.data = barChartData

Output of the above sample code.

Upvotes: 3

Related Questions