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

Issue with centering xAxis labels #1750

Closed
BillNattaner opened this issue Oct 31, 2016 · 1 comment
Closed

Issue with centering xAxis labels #1750

BillNattaner opened this issue Oct 31, 2016 · 1 comment

Comments

@BillNattaner
Copy link

Howdy, and thank you for the code!

In issue 'Strings for Labels in Swift 3 #1732' (closed as of about 3 hours ago) the idea was to compare "my scores" for each of the last four weeks with "group scores" for the same period in a bar chart. The writer especially wanted to see how to put labels on the x-axis ("week -3", "week -2", "last week" and "this week"). Good news: below is a solution that shows the graph with the desired labels. Bad news: I have not been able to get the labels to center and, when centerAxisLabelsEnabled is set to true, the axis formatter starts receiving an index value of -1.

scorechart

Details:
I'm using Xcode 8, Swift 3, and used cocoaPods to bring in the most recent version of iOS Charts. The code below is all kept in the ViewController file of a single-view app. The only object in that single view is an instance of BarChartView.

Axis Formatter Class

class WeekAxisValueFormatter: NSObject, IAxisValueFormatter {
    
    static let weeks = [ "-3", "-2", "Last", "This" ]       // xAxis labels
    static let labelCount = weeks.count                     // count of xAxis labels
    static var indexCount = 0                               // for debug reporting, below

    func stringForValue(_ value: Double,
                        axis: AxisBase?) -> String {
        let index = Int( value )
        
        // FIXME:   an invalid index is sometimes encountered, report all indices here.
        //          Remove the defensive stuff once the bug is identified.
        WeekAxisValueFormatter.indexCount += 1
        print( "\( WeekAxisValueFormatter.indexCount ). Value of index: \( index )")
        
        if index < 0 || index >= WeekAxisValueFormatter.weeks.count { return "" }
        return WeekAxisValueFormatter.weeks[ index ]
    }
}

ViewController class

class ViewController: UIViewController {

    @IBOutlet weak var scoresBarChart: BarChartView!
    
    private struct Lets {
        // data for chart
        static let myScores         = [85, 86, 87, 89]
        static let groupScores      = [90, 95, 90, 95]
        // chart text
        static let myScoreLabel     = "My Score"
        static let groupScoreLabel  = "Group Score"
        static let description      = "week by week score comparison"
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Chart Properties (per BarChartViewController.m in ChartsDemo)
        let cht = self.scoresBarChart!
        cht.drawBarShadowEnabled = false                // display gray bars indicating max values
        cht.drawValueAboveBarEnabled = true             // display value above bar-top not below bar-top
        cht.maxVisibleCount = 6                         // max no. of visible drawn values ??
        
        // Chart Properties (beyond what is used in ChartsDemo, for legibility)
        cht.backgroundColor = UIColor.lightGray         // highlights the view containing the graph
        cht.chartDescription?.text = Lets.description   // appears as graph comment
        cht.chartDescription?.textColor = UIColor.blue  // color of graph comment
        
        // X-Axis Properties (per BarChartViewController.m in ChartsDemo)
        let xAxis = scoresBarChart!.xAxis
        xAxis.labelPosition = XAxis.LabelPosition.bottom        // place labels below X-axis
        xAxis.labelFont = UIFont.systemFont( ofSize: 10 )       // label formatting
        xAxis.drawGridLinesEnabled = false                      // do not show vertical grid lines
        xAxis.granularity = 1.0                                 // min. interval between axis values ??
        xAxis.labelCount = WeekAxisValueFormatter.labelCount    // number of X-values must be in 2...25
        xAxis.valueFormatter =
            WeekAxisValueFormatter()                            // puts strings onto X-axis
        
        // X-Axis Properties (beyond what is used in ChartsDemo, to center)
        xAxis.centerAxisLabelsEnabled = true                  // added to overcome label misplacement
//        xAxis.granularityEnabled = true                       // might activate granularity ??
//        xAxis.axisMaximum = 1.0                               // added per sree127 in issue 1556
//        xAxis.axisMinimum = 1.0                               // added per sree127 in issue 1556
        
        // BarChartData setup ( per BarChartViewController.m in ChartsDemo )
        let yVals1 = Lets.myScores.enumerated().map {
            ( index, score ) in
            return BarChartDataEntry(x: Double( index) , y: Double( score ))
        }
        let set1 = BarChartDataSet( values: yVals1, label: Lets.myScoreLabel )
        set1.colors = [ UIColor.orange ]
        
        let yVals2 = Lets.groupScores.enumerated().map {
            ( index, score ) in
            return BarChartDataEntry( x: Double( index ), y: Double( score ))
        }
        let set2 = BarChartDataSet( values: yVals2, label: Lets.groupScoreLabel )
        set2.colors = [ UIColor.green ]
        set2.drawValuesEnabled = false
        let dataSets = [ set1, set2 ]

        let data = BarChartData( dataSets: dataSets )
        data.barWidth = 0.3                             // width in values (not pixels)
        data.groupBars( fromX: 0.015,                   // spacing begins
                        groupSpace: 0.15,               // spacing between groups of bars
                        barSpace: 0.05 )                // spacing between individual bars
        scoresBarChart.data = data
        
        // debugging (uses BarChartData extension, not shown)
        print( "\n\( data ) \n" )
    }
}

Every time the X-axis-formatter is invoked it prints the index parameter to the log. Also, immediately after the scoresBarChart.data is set, the properties of the BarChartData instance is also logged. The log looks like this:

1. Value of index: 0
2. Value of index: 1
3. Value of index: 2
4. Value of index: 3
5. Value of index: 0
6. Value of index: 1
7. Value of index: 2
8. Value of index: 3

*** CHART DATA:
	barWidth:.....0.3
	yMax:.........95.0
	yMin:.........85.0
	xMax:.........3.0
	xMin:.........0.0
	dataSetCount: 2
	*** SET: My Score
		yMin:...............85.0
		yMax:...............89.0
		xMin:...............0.0
		xMax:...............3.0
		isHighlightEnabled: true
		needsFormatter:.....false
		isDrawEnabled:......true
		visible:............true
		entryCount:.........4
	*** SET: Group Score
		yMin:...............90.0
		yMax:...............95.0
		xMin:...............0.0
		xMax:...............3.0
		isHighlightEnabled: true
		needsFormatter:.....false
		isDrawEnabled:......false
		visible:............true
		entryCount:.........4
	*** All Entries in All ChartDataSets
		Labels   My Score   Group Score   
		-3       85.0       90.0          
		-2       86.0       95.0          
		Last     87.0       90.0          
		This     89.0       95.0           

9. Value of index: -1
10. Value of index: 0
11. Value of index: 1
12. Value of index: 2
13. Value of index: 3
14. Value of index: 4
15. Value of index: -1
16. Value of index: 0
17. Value of index: 1
18. Value of index: 2
19. Value of index: 3

I'm guessing that the underlying logic is making two attempts to format the chart before it gets the data (once for each axis?) and then tries again after getting the data. The issue with an index of -1 in the X-axis label formatter goes away when the xAxis.centerAxisLabelsEnabled = true line is commented out.

What am I overlooking?

Thanks again,
Bill

@danielgindi
Copy link
Collaborator

Well it depends on the axis ranges. If you have padding for example, or custom axisMin/axisMax, or maybe other variations, then it would make other x-values possible.

You have to keep in mind that the x-value to format is not an index, but a value. You should be able to format decimal values to labels (granularity should constrain fractions), and you should also be able to format -1 or 9999. Even if you do not have that in your array of pre-defined labels.
Because you don't have the value in your array, you may decide that the String for -1 should be ""...

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

2 participants