Search code examples
iosswiftswift5ios-charts

IOS Charts - YAxis scaling number round to nearest hundred, thousands, etc


Is there a way with the iOs Charts lib (3.6.0) to scale the Y axis with the nearest hundred, thousands, etc ?

I'm probably missing something but my Y axis doesn't make any sense :

enter image description here

In this exemple, I would like my Y axis to be something like :

75 000
60 000
45 000
30 000
15 000
0
-15 000

I tried using autoScaleMinMaxEnabledbut it doesn't seem to work.

I don't understand how to make the y axis use rounded numbers ?

Thank you for your help

Update :

Here is the code I use, as requested :

class GraphCustomView : LineChartView, ChartViewDelegate {
    //MARK: Variables
    var graphModel: [GraphModel] = []
    
    // Initialize Chart
    public func initializeChart(graphModel: [GraphModel], devise: String, withouthFill: Bool? = true) {
        self.graphModel = [GraphModel]()
        self.graphModel = graphModel
        
        self.delegate = self
        self.chartDescription?.enabled = false
                
        // xAxis
        self.xAxis.enabled = true
        self.xAxis.drawGridLinesEnabled = false
        self.xAxis.labelPosition = .bottom
        self.xAxis.valueFormatter = DateValueFormatter(miniTime: graphModel.first!.miniDate)
        self.xAxis.labelFont = UIFont(name: "Ubuntu", size: 8)!
        self.xAxis.labelRotationAngle = -20
        self.xAxis.granularityEnabled = true
        self.xAxis.granularity = 1
        
        // RightAxis
        self.rightAxis.enabled = false
        self.rightAxis.drawGridLinesEnabled = false
        
        // LeftAxis
        self.leftAxis.drawGridLinesEnabled = true
        self.leftAxis.gridColor = .gray
        self.leftAxis.gridLineDashLengths = [CGFloat(1.5)]
        self.leftAxis.labelFont = UIFont(name: "Ubuntu", size: 8)!
        
        // General chart settings
        self.drawBordersEnabled = false
        self.setScaleEnabled(true)
        
        leftAxis.setLabelCount(7, force: true)
        leftAxis.drawTopYLabelEntryEnabled = true
        leftAxis.drawBottomYLabelEntryEnabled = true
               
        // Legend
        self.leftAxis.labelFont = UIFont(name: "Ubuntu", size: 11)!
        self.xAxis.labelFont = UIFont(name: "Ubuntu", size: 11)!
        
        let l = self.legend
        l.form = .circle
        l.horizontalAlignment = .right
        l.verticalAlignment = .bottom
        l.orientation = .horizontal
        l.drawInside = false
        l.font = UIFont(name: "Ubuntu", size: 8)!
        
        // Overdrawn line
        let minValue = graphModel.first!.minValue
        let maxValue = graphModel.first!.maxValue
        var range: Double = 0.0
        
        if (maxValue - minValue != 0) {
            range = (maxValue - minValue) / 7
        }
        
        if (graphModel.first!.minValue - (range * 2) < graphModel.first!.overdrawn && graphModel.first!.overdrawn < graphModel.first!.minValue) {
            self.leftAxis.axisMinimum = graphModel.first!.overdrawn
        }
                                
        let limit: ChartLimitLine = ChartLimitLine()
        limit.label = "Overdrawn"
        limit.limit = graphModel.first!.overdrawn
        limit.valueTextColor = .red
        limit.lineColor = .red
        limit.lineDashLengths = [10, 5]
        leftAxis.addLimitLine(limit)
        
        // Marker/Tooltip
        let marker = XYMarkerView(color: .darkGray,
                                  font: UIFont(name: "Ubuntu", size: 12)!,
                                  textColor: .white,
                                  insets: UIEdgeInsets(top: 8, left: 8, bottom: 20, right: 8),
                                  xAxisValueFormatter: DateValueFormatter(miniTime: graphModel.first!.miniDate),
                                  yAxisValueFormatter: MarkerValueFormatter(devise: devise))
        marker.chartView = self
        marker.minimumSize = CGSize(width: 80, height: 40)
        self.marker = marker
        
        self.isMultipleTouchEnabled = true
        self.highlightPerTapEnabled = true
        self.highlightPerDragEnabled = true
        
        // Add data to chart
        self.updateChartData()
    }
    
    public func updateChartData() {
        // Creating dataSets
        var dataSets: [LineChartDataSet] = [LineChartDataSet]()
        
        // We need to sort the data in order to add it to the graph
        for graph in self.graphModel {
            graph.chartData.sort(by: { $0.x < $1.x })
            
            let dataSet : LineChartDataSet = LineChartDataSet(entries: graph.chartData, label: graph.name)
            dataSet.circleRadius = 0
            dataSet.colors = graph.color
            dataSet.drawValuesEnabled = false
            dataSet.lineWidth = 2
            dataSet.mode = .cubicBezier

            let leftAxis = self.leftAxis
            leftAxis.drawLimitLinesBehindDataEnabled = true
            dataSet.fillColor = graph.color.first!
            dataSet.drawFilledEnabled = true
            
            // Add to datasets
            dataSets.append(dataSet)
        }
        
        // Styling data
        let data = LineChartData(dataSets: dataSets)
        data.setValueFont(UIFont(name: "Ubuntu", size: 7)!)
        
        // Displaying graph
        self.data = data
    }

Here is the data I used for this specific chart :

[{\"pointDate\":\"2016-11-01T00:00:00\",\"pointValue\":45375.800000000003},{\"pointDate\":\"2016-11-01T00:00:00\",\"pointValue\":45375.800000000003},{\"pointDate\":\"2016-10-31T00:00:00\",\"pointValue\":54755.580000000002},{\"pointDate\":\"2016-10-30T00:00:00\",\"pointValue\":34324.199999999997},{\"pointDate\":\"2016-10-29T00:00:00\",\"pointValue\":43846.059999999998},{\"pointDate\":\"2016-10-28T00:00:00\",\"pointValue\":29243.779999999999},{\"pointDate\":\"2016-10-27T00:00:00\",\"pointValue\":28238.439999999999},{\"pointDate\":\"2016-10-26T00:00:00\",\"pointValue\":-11461.74},{\"pointDate\":\"2016-10-25T00:00:00\",\"pointValue\":-5756.5500000000002},{\"pointDate\":\"2016-10-24T00:00:00\",\"pointValue\":27565.939999999999},{\"pointDate\":\"2016-10-23T00:00:00\",\"pointValue\":40770.910000000003},{\"pointDate\":\"2016-10-22T00:00:00\",\"pointValue\":46875.870000000003},{\"pointDate\":\"2016-10-21T00:00:00\",\"pointValue\":44609.540000000001},{\"pointDate\":\"2016-10-20T00:00:00\",\"pointValue\":43970.769999999997},{\"pointDate\":\"2016-10-19T00:00:00\",\"pointValue\":43262.910000000003},{\"pointDate\":\"2016-10-18T00:00:00\",\"pointValue\":22016.080000000002},{\"pointDate\":\"2016-10-17T00:00:00\",\"pointValue\":45001.940000000002},{\"pointDate\":\"2016-10-16T00:00:00\",\"pointValue\":44443.739999999998},{\"pointDate\":\"2016-10-15T00:00:00\",\"pointValue\":61521.720000000001},{\"pointDate\":\"2016-10-14T00:00:00\",\"pointValue\":31674.16},{\"pointDate\":\"2016-10-13T00:00:00\",\"pointValue\":42767.040000000001},{\"pointDate\":\"2016-10-12T00:00:00\",\"pointValue\":55202.110000000001},{\"pointDate\":\"2016-10-11T00:00:00\",\"pointValue\":76274.100000000006},{\"pointDate\":\"2016-10-10T00:00:00\",\"pointValue\":39097.07},{\"pointDate\":\"2016-10-09T00:00:00\",\"pointValue\":13742.99},{\"pointDate\":\"2016-10-08T00:00:00\",\"pointValue\":9616.2199999999993},{\"pointDate\":\"2016-10-07T00:00:00\",\"pointValue\":1450.6099999999999},{\"pointDate\":\"2016-10-06T00:00:00\",\"pointValue\":5820.5},{\"pointDate\":\"2016-10-05T00:00:00\",\"pointValue\":-9522.75},{\"pointDate\":\"2016-10-04T00:00:00\",\"pointValue\":8317},{\"pointDate\":\"2016-10-03T00:00:00\",\"pointValue\":7923.6499999999996},{\"pointDate\":\"2016-10-02T00:00:00\",\"pointValue\":10249.889999999999},{\"pointDate\":\"2016-10-01T00:00:00\",\"pointValue\":2388.6799999999998},{\"pointDate\":\"2016-09-30T00:00:00\",\"pointValue\":0}]

And the code use to format it a bit :

for point in pointevo.pointEvolution {
    let date = point.pointDate.toDate()
    let timeInSecondes = date?.timeIntervalSince1970
                                    
    if (firstTime) {
        self.miniDate = timeInSecondes!
        firstTime = false
        
        minValue = point.pointValue
        maxValue = point.pointValue
    }
    
    if (point.pointValue < minValue) {
        minValue = point.pointValue
    }
    if (point.pointValue > maxValue) {
        maxValue = point.pointValue
    }
    
    chartData.append(ChartDataEntry(x: (timeInSecondes! - miniDate) / (3600.0 * 24.0), y: point.pointValue))
                        
    if tempPointValue.firstIndex(of: point.pointValue) == nil {
        tempPointValue.append(point.pointValue)
    }
}

Solution

  • EDITED AFTER DISCUSSIONS

    As I test your code with your data I found the root of your issue. In your code it's the line:

    leftAxis.setLabelCount(7, force: true)
    

    If force parameter is set to true - it override the default calculated labels and force the labels to be exact number of labels you specified and it will spread by equal distance.

    If not force it - when there will be not exact number of labels specified, I got 6 lines (and labels). So it's now up to you how to better use it.

    Here also examples of results if force parameter is true and false (I set here xaxis and overdraw values to some static values).

    force parameter is true

    force parameter is false

    Below the first one answer for some other situation.

    You can use for it IAxisValueFormatter protocol method stringForValue(_ value:, axis:). You can round your value here and place additional symbols, like currencies symbol, like I do. Something like this:

    extension ChartController: IAxisValueFormatter {
    
        func stringForValue(_ value: Double, axis: AxisBase?) -> String {
            if axis is XAxis {
                // do something you need for X axis
                return valueTransformedToString
            } else {
                // do something you need for Y axis
                return valueTransformedToString
            }
        }
    
    }