ios 图表 3.0 - 将 x 标签(日期)与图表对齐

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

我很难将图表库(来自 Daniel Gindi)从版本 2 (Swift 2.3) 迁移到版本 3 (Swift 3)。

基本上,我无法将 x 标签(日期)与相应的图正确对齐。

这是我之前的版本2:

在版本 2 中,我有第 7、8、10 和 11 天的值。 所以我在中间错过了一天,但标签与地块正确对齐。


这是我在版本 3 中的内容:

在版本 3 中,x 轴中的 "labels" 现在已替换为双精度(对于日期,它是自 1970 年以来的 timeInterval),并通过格式化程序进行格式化。 所以,不可否认,图表现在更多 "correct",因为图表正确地推断了第 9 个值,但我找不到如何将标签放在相应的图下。

这是我的 x 轴代码:

 let chartView = LineChartView()
 ...
 let xAxis = chartView.xAxis
 xAxis.labelPosition = .bottom
 xAxis.labelCount = entries.count
 xAxis.drawLabelsEnabled = true
 xAxis.drawLimitLinesBehindDataEnabled = true
 xAxis.avoidFirstLastClippingEnabled = true

 // Set the x values date formatter
 let xValuesNumberFormatter = ChartXAxisFormatter()
 xValuesNumberFormatter.dateFormatter = dayNumberAndShortNameFormatter // e.g. "wed 26"
 xAxis.valueFormatter = xValuesNumberFormatter

这是我创建的 ChartXAxisFormatter class:

import Foundation
import Charts

class ChartXAxisFormatter: NSObject {
    var dateFormatter: DateFormatter?
}

extension ChartXAxisFormatter: IAxisValueFormatter {

    func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        if let dateFormatter = dateFormatter {

            let date = Date(timeIntervalSince1970: value)
            return dateFormatter.string(from: date)
        }

        return ""
    }

}

所以,这里的值是正确的,格式是正确的,图表的形状是正确的,但是标签与相应图表的对齐不好。 感谢您的帮助

好的,知道了!

您必须定义参考时间间隔(x 轴的“0”)。然后计算每个x值的附加时间间隔。

ChartXAxisFormatter 变为:

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)
    }

}

然后,当我创建数据条目时,它的工作方式如下:

// (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 { [=11=].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 ...

结果好多了(顺便去掉了cubic):

如果你确切地知道你需要在x轴上有多少个标签,你可以写这个代码来解决it.For例子,如果我需要七个标签出现在x轴上,那么这应该是 enough.The 原因是图表库没有正确计算两个 x 轴点之间的间隔,因此绘图标签 mismatch.When 我们强制图表库根据给定数量的标签进行绘图,问题好像没了

 let xAxis = chart.xAxis
 xAxis.centerAxisLabelsEnabled = false
 xAxis.setLabelCount(7, force: true) //enter the number of labels here

我用这个答案解决了这个问题

我怀疑这些偏移量来自将 X 轴值调整到我的一天中的特定时间

这是我的代码

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
}

@IBOutlet weak var tView:UIView!
@IBOutlet weak var lineChartView:LineChartView!{
    didSet{
        lineChartView.xAxis.labelPosition = .bottom
        lineChartView.xAxis.granularityEnabled = true
        lineChartView.xAxis.granularity = 1.0

        let xAxis = lineChartView.xAxis
        //            xAxis.axisMinimum = 0.0
        //            xAxis.granularity = 1.0
        //            xaAxis.setLabelCount(6, force: true)
    }
}

@IBOutlet weak var back: UIButton?
@IBAction func back(_ sender: Any) {
    self.navigationController?.popViewController(animated: true)
}

override func viewDidLoad() {
    super.viewDidLoad()

    self.lineChartView.delegate = self
    self.lineChartView.chartDescription?.textColor = UIColor.white

    let months = ["Jan" , "Feb", "Mar"]
    let dollars1 = [1453.0,2352,5431]
    setChart(months, values: dollars1)
}

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

    var dataEntries: [ChartDataEntry] = []

    for i in 0 ..< dataPoints.count {
        dataEntries.append(ChartDataEntry(x: Double(i), y: values[i]))
    }

    let lineChartDataSet = LineChartDataSet(entries: dataEntries, label: nil)
    lineChartDataSet.axisDependency = .left
    lineChartDataSet.setColor(UIColor.black)
    lineChartDataSet.setCircleColor(UIColor.black) // our circle will be dark red
    lineChartDataSet.lineWidth = 1.0
    lineChartDataSet.circleRadius = 3.0 // the radius of the node circle
    lineChartDataSet.fillAlpha = 1
    lineChartDataSet.fillColor = UIColor.black
    lineChartDataSet.highlightColor = UIColor.white
    lineChartDataSet.drawCircleHoleEnabled = true

    var dataSets = [LineChartDataSet]()
    dataSets.append(lineChartDataSet)


    let lineChartData = LineChartData(dataSets: dataSets)
    lineChartView.data = lineChartData
    lineChartView.rightAxis.enabled = false
    lineChartView.xAxis.drawGridLinesEnabled = false
    lineChartView.xAxis.labelPosition = .bottom
    lineChartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: dataPoints)
}


func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
    print(entry)
}