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

Invert xAxis label #2504

Closed
sahabe1 opened this issue Jun 6, 2017 · 9 comments
Closed

Invert xAxis label #2504

sahabe1 opened this issue Jun 6, 2017 · 9 comments

Comments

@sahabe1
Copy link

sahabe1 commented Jun 6, 2017

Ho to invert xaxis label. currently it showing 0-----28 but my requirement is 28-----0
please see attachment

img_0154

@liuxuan30
Copy link
Member

you can use valueFormatter to reorder it.

@HamGuy
Copy link

HamGuy commented Jul 9, 2018

updated: I found a similar issue at stackoverflow, but the delegate method in valueFormatter do not provide the index of the value.


@liuxuan30 I used the xAxis to show weekdays(start of week to the end) with a LineChartView in my case, if the left value smaller than the bright in xAxis, it works well, but if not, as the following example showed, the label in xAxis dissappeared

normal aXis( (from 2018/7/22 to 2018/7/28)): 22 23 24 25 26 27 28
image

but the next week form 2018/8/29 to 2018/08/04
image

if I comment the Line of xAxis.forceLabelsEnabled = true, the value disappeared

        let from = allDays.first ?? 0
        let to = allDays.last ?? 0
        xAxis.axisMinimum = from
        xAxis.axisMaximum = to
//         xAxis.forceLabelsEnabled = true
xAxis.calculate(min: from, max: to)

image

how can I fixed it?

@pmairoldi
Copy link
Collaborator

You can reverse your dataset so that they are in the correct order. Your last value should have an x index of 0 and so on.

@HamGuy
Copy link

HamGuy commented Jul 19, 2018

@petester42 Well, it is not work well in my case. I should keep the weekday order for a week(e.g 7/28, 7/29/, 7/30, /7/31, 8/1/,8/2,/8/3) on the x Axis, so there was no an x index of 0.

After step by step debugging, I found that when the entries of x Axis changes liked the [28,29,30,31,1,2,3], that the 1st one is bigger then the last one in digital, the entries value also changed in the getFormattedLabel in AxisBase.swift, it keeps the last y Axis's values.

I'd like to know if possible that render the x Axis with a string value from the input value, not the digital. just keep the original orders.
Here is my demo project on dropbox cause that there was something wrong with my network and I can't push my code to Github.

import UIKit
import Charts

class ViewController: UIViewController {

    @IBOutlet weak var chartView: BarChartView!
    
    private(set) lazy var xAxis: XAxis = {
        let xAxis = chartView.xAxis
        xAxis.labelFont = .systemFont(ofSize: 11)
        xAxis.labelTextColor = .darkText
        xAxis.drawAxisLineEnabled = true
        xAxis.labelPosition = .bottom
        xAxis.drawGridLinesEnabled = false
        xAxis.granularityEnabled = true
        return xAxis
    }()
    
    private lazy var leftYAxis: YAxis = {
        let leftAxis = chartView.leftAxis
        leftAxis.drawGridLinesEnabled = false
        leftAxis.granularityEnabled = false
        return leftAxis
    }()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initChartView()
        loadData()
    }
    
    func initChartView() {
        chartView.chartDescription?.enabled = false
        chartView.dragEnabled = true
        chartView.setScaleEnabled(true)
        chartView.pinchZoomEnabled = false
        
        let l = chartView.legend
        l.form = .circle
        l.font = .systemFont(ofSize: 11)!
        l.textColor = .darkText
        l.horizontalAlignment = .center
        l.verticalAlignment = .top
        l.orientation = .horizontal
        l.yEntrySpace = 20
        l.drawInside = false
        
        chartView.rightAxis.enabled = false
        
        xAxis.labelCount = 7
        
        leftYAxis.axisMinimum = 0
        leftYAxis.axisMaximum = 70
    }

    
    func loadData() {
        let allDays:[Double] = [28,29,30,31,1,2,3,4] // not show xlabel
        
        //let allDays:[Double] = [16,17,18,19,20,21,22] // show x label
        
        xAxis.axisMinimum = allDays.first ?? 0
        xAxis.axisMaximum = allDays.last ?? 0
        
        var values:[BarChartDataEntry] = []
        for day in allDays {
            let value = BarChartDataEntry(x: day, y: Double(Int.randomIntNumber(lower: 0, upper: 70)))
            values.append(value)
        }
        
        let set = BarChartDataSet(values: values, label: "")
        let data = BarChartData(dataSets: [set])
        chartView.data = data
        chartView.animate(xAxisDuration: 1.0)
    }
}


extension Int {
    public static func randomIntNumber(lower: Int = 0,upper: Int = Int(UInt32.max)) -> Int {
        return lower + Int(arc4random_uniform(UInt32(upper - lower)))
    }
}

@pmairoldi
Copy link
Collaborator

The reason that didn't work is that the axisMin is greater than the axis max. This is about what I meant when I meant use an index:

import UIKit
import Charts

class ViewController: UIViewController {

    @IBOutlet weak var chartView: BarChartView!
    
    private(set) lazy var xAxis: XAxis = {
        let xAxis = chartView.xAxis
        xAxis.labelFont = .systemFont(ofSize: 11)
        xAxis.labelTextColor = .darkText
        xAxis.drawAxisLineEnabled = true
        xAxis.labelPosition = .bottom
        xAxis.drawGridLinesEnabled = false
        xAxis.granularityEnabled = true
        return xAxis
    }()
    
    private lazy var leftYAxis: YAxis = {
        let leftAxis = chartView.leftAxis
        leftAxis.drawGridLinesEnabled = false
        leftAxis.granularityEnabled = false
        return leftAxis
    }()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initChartView()
        loadData()
    }
    
    func initChartView() {
        chartView.chartDescription?.enabled = false
        chartView.dragEnabled = true
        chartView.setScaleEnabled(true)
        chartView.pinchZoomEnabled = false
        
        let l = chartView.legend
        l.form = .circle
        l.font = .systemFont(ofSize: 11)!
        l.textColor = .darkText
        l.horizontalAlignment = .center
        l.verticalAlignment = .top
        l.orientation = .horizontal
        l.yEntrySpace = 20
        l.drawInside = false
        
        chartView.rightAxis.enabled = false
        
        xAxis.labelCount = 7

        leftYAxis.axisMinimum = 0
        leftYAxis.axisMaximum = 70
    }

    
    func loadData() {
//        let allDays:[Int] = [28,29,30,31,1,2,3,4] // not show xlabel
        let allDays:[Int] = [28,29,30,31,32,33,34] // show x label

        var values:[BarChartDataEntry] = []
        for (index, day) in allDays.enumerated() {
            let value = BarChartDataEntry(x: Double(index), y: Double(Int.randomIntNumber(lower: 0, upper: 70)), data: day as AnyObject)
            values.append(value)
        }

        xAxis.valueFormatter = DayAxisFormatter(values);

        let set = BarChartDataSet(values: values, label: "")
        let data = BarChartData(dataSets: [set])
        chartView.data = data
        chartView.animate(xAxisDuration: 1.0)
    }
}


extension Int {
    public static func randomIntNumber(lower: Int = 0,upper: Int = Int(UInt32.max)) -> Int {
        return lower + Int(arc4random_uniform(UInt32(upper - lower)))
    }
}

class DayAxisFormatter: IAxisValueFormatter {
    private let values: [BarChartDataEntry];

    init(_ values: [BarChartDataEntry]) {
        self.values = values
    }

    func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        let index = Int(value)
        let entry = values[index];

        guard let day = entry.data as! Int? else {
            fatalError("whoops")
        }

        return "\(day)"
    }
}

Play around with something like that and you'll get something you need.

@pmairoldi
Copy link
Collaborator

As for the original question from @sahabe1, the same thing can be done just reverse your dataset. Same code as above but replace loadData with the following:

 
    func loadData() {
//        let allDays:[Int] = [28,29,30,31,1,2,3,4] // not show xlabel
        let allDays:[Int] = [28,29,30,31,32,33,34] // show x label

        var values:[BarChartDataEntry] = []
        for (index, day) in allDays.reversed().enumerated() {
            let value = BarChartDataEntry(x: Double(index), y: Double(Int.randomIntNumber(lower: 0, upper: 70)), data: day as AnyObject)
            values.append(value)
        }

        xAxis.valueFormatter = DayAxisFormatter(values);

        let set = BarChartDataSet(values: values, label: "")
        let data = BarChartData(dataSets: [set])
        chartView.data = data
        chartView.animate(xAxisDuration: 1.0)
    }

@HamGuy
Copy link

HamGuy commented Jul 20, 2018

Well, One more question, could you give me some tips that where should I start with to support axisMin is greater than the axis max and keeps the original values orders.

@pmairoldi
Copy link
Collaborator

In my first example I didn’t change the order. Without doing something like I just posted it’s not possible in charts. Charts is based on the numeric values only and not the semantic values such as days, dates, etc. 4 can’t come after 31, it doesn’t make sense in terms of a numerical graph. Look at that date label demos in the demo projects to get a better idea of how to work around this limitation. It’s a more complex example of what I just posted here.

@HamGuy
Copy link

HamGuy commented Jul 20, 2018

Thanks for the tips so much.

I made a stupid mistake, all I want is just show the day string on the x Axis, not keep the data entry's x value as the day.

    private var lables: [String] = []
    
    func loadData() {
        let indexes: [Double] = [0,1,2,3,4,5,6]
       
        xAxis.valueFormatter = self
        lables = [28,29,30,31,1,2,3].map {"\($0)"}
        
        xAxis.axisMinimum = Double(indexes.first ?? 0)
        xAxis.axisMaximum = Double(indexes.last ?? 0)
        
        var values:[BarChartDataEntry] = []
        for index in indexes {
            let value = BarChartDataEntry(x: index, y: Double(Int.randomIntNumber(lower: 0, upper: 70)))
            values.append(value)
        }
        
        let set = BarChartDataSet(values: values, label: "")
        let data = BarChartData(dataSets: [set])
        chartView.data = data
        chartView.animate(xAxisDuration: 1.0)
    }
}


extension ViewController: IAxisValueFormatter {
    func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        let index = Int(value)
        return lables[index]
    }
}

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

4 participants