ios Charts 3.0 - Align x labels (dates) with plots

后端 未结 4 1747
渐次进展
渐次进展 2020-12-09 20:05

I am having a hard time to migrate library Charts (from Daniel Gindi) from version 2 (Swift 2.3) to 3 (Swift 3).

Basically, I can\'t have the x labels (dates) aligne

相关标签:
4条回答
  • here is the Solution of xValuesNumberFormatter

    let xValuesFormatter = DateFormatter()
        xValuesFormatter.dateFormat = "MM/dd"
    let xValuesNumberFormatter = ChartXAxisFormatter(referenceTimeInterval: referenceTimeInterval, dateFormatter: xValuesFormatter)
        xValuesNumberFormatter.dateFormatter = xValuesFormatter // e.g. "wed 26"
        xAxis.valueFormatter = xValuesNumberFormatter
    
    0 讨论(0)
  • 2020-12-09 20:13

    OK, got it!

    You've got to define a reference time Interval (the "0" for the x axis). And then calculate the additional time interval for each x value.

    The ChartXAxisFormatter becomes:

    import Foundation
    import Charts
    
    class ChartXAxisFormatter: NSObject {
        fileprivate var dateFormatter: DateFormatter?
        fileprivate var referenceTimeInterval: TimeInterval?
    
        convenience init(referenceTimeInterval: TimeInterval, dateFormatter: DateFormatter) {
            self.init()
            self.referenceTimeInterval = referenceTimeInterval
            self.dateFormatter = dateFormatter
        }
    }
    
    
    extension ChartXAxisFormatter: IAxisValueFormatter {
    
        func stringForValue(_ value: Double, axis: AxisBase?) -> String {
            guard let dateFormatter = dateFormatter,
            let referenceTimeInterval = referenceTimeInterval
            else {
                return ""
            }
    
            let date = Date(timeIntervalSince1970: value * 3600 * 24 + referenceTimeInterval)
            return dateFormatter.string(from: date)
        }
    
    }
    

    And, then, when I create my data entries, it works like so:

    // (objects is defined as an array of struct with date and value)
    
    // Define the reference time interval
    var referenceTimeInterval: TimeInterval = 0
    if let minTimeInterval = (objects.map { $0.date.timeIntervalSince1970 }).min() {
            referenceTimeInterval = minTimeInterval
        }
    
    
        // Define chart xValues formatter
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        formatter.timeStyle = .none
        formatter.locale = Locale.current
    
        let xValuesNumberFormatter = ChartXAxisFormatter(referenceTimeInterval: referenceTimeInterval, dateFormatter: formatter)
    
    
    
        // Define chart entries
        var entries = [ChartDataEntry]()
        for object in objects {
            let timeInterval = object.date.timeIntervalSince1970
            let xValue = (timeInterval - referenceTimeInterval) / (3600 * 24)
    
            let yValue = object.value
            let entry = ChartDataEntry(x: xValue, y: yValue)
            entries.append(entry)
        }
    
    // Pass these entries and the formatter to the Chart ...
    

    The result is much nicer (I removed cubic by the way):

    0 讨论(0)
  • 2020-12-09 20:29

    If you exactly know how many labels you need in the x-axis,you can write this code to solve it.For example,If I need seven labels to appear on the x-axis,Then this should be enough.The reason is the chart library is not properly calculating the intervals between the two x-axis points and hence the plot-label mismatch.When we force the library to plot against the given number of labels,The issue seems to be gone.

     let xAxis = chart.xAxis
     xAxis.centerAxisLabelsEnabled = false
     xAxis.setLabelCount(7, force: true) //enter the number of labels here
    
    0 讨论(0)
  • 2020-12-09 20:29

    I solved this issue using this answer https://stackoverflow.com/a/44554613/2087608

    I suspected that these offsets come from adjusting X axis value to a specific time of the day in my case

    Here is my code

    for i in 0..<valuesViewModel.entries.count {
      let dataEntry = ChartDataEntry(x: roundDate(date: valuesViewModel.entries[i].date).timeIntervalSince1970, y: valuesViewModel.entries[i].value)
      dataEntries.append(dataEntry)
    }
    
    
    
    func roundDate(date: Date) -> Date {
      var comp: DateComponents = Calendar.current.dateComponents([.year, .month, .day], from: date)
      comp.timeZone = TimeZone(abbreviation: "UTC")!
      let truncated = Calendar.current.date(from: comp)!
      return truncated
    }
    
    0 讨论(0)
提交回复
热议问题