【问题标题】:ios Charts 3.0 - Align x labels (dates) with plotsios Charts 3.0 - 将 x 标签(日期)与图对齐
【发布时间】:2017-01-18 13:15:27
【问题描述】:

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

基本上,我不能让 x 标签(日期)与相应的图正确对齐。

这是我之前的版本 2:

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


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

在版本 3 中,x 轴上的“标签”现在已被替换为双精度(对于日期,它是自 1970 年以来的 timeInterval),并通过格式化程序进行格式化。 所以,不可否认,现在图表更“正确”了,因为图表正确地推断了第 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 类:

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

}

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

【问题讨论】:

  • 我能够使用 Charts 3.0.0 和 Charts 3.0.1 进行重现。同时,我在一个不受这些问题困扰的单独项目中使用了 Charts 3.0.0 的代码。也许图表设置的某些组合会显示此错误?
  • 我收到了 Charts Github repo 的回复:显然,在 x 轴上使用 时间间隔 时会发生这种情况,因为 Charts 使用插值来确定要显示的标签。
  • 错位的原因是 xAxis.avoidFirstLastClippingEnabled = true

标签: swift ios-charts


【解决方案1】:

好的,明白了!

您必须定义一个参考时间间隔(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 { $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 ...

结果要好得多(顺便删除了三次):

【讨论】:

  • 我想这就是我所做的
  • 什么是 xValuesFormatter?
  • 我仍然无法解决这个问题。你能解释清楚吗?谢谢
  • @FrédéricAdda 回答中缺少一些我想澄清的内容:(1)我认为您的意思是将formatter 传递给xValuesNumberFormatter 而不是xValuesFormatter(2)代码没有显示它,但需要在图表上设置 xValuesNumberFormatter....换句话说:yourChartView.xAxis.valueFormatter = xValuesNumberFormatter
  • @GarySabo 你能在我的link 上建议吗?
【解决方案2】:

如果你确切知道在x轴上需要多少个标签,你可以编写这段代码来解决它。例如,如果我需要在x轴上出现七个标签,那么这应该足够了。原因是图表库没有正确计算两个 x 轴点之间的间隔,因此绘图标签不匹配。当我们强制库针对给定数量的标签进行绘图时,问题似乎消失了。

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 为什么是两个“Jan”?这段代码有错误吗?
    • 我已经更新了答案并删除了错误
    【解决方案4】:

    这里是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
    

    【讨论】:

      【解决方案5】:

      我使用这个答案解决了这个问题 https://stackoverflow.com/a/44554613/2087608

      在我的情况下,我怀疑这些偏移来自将 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
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-10
        • 2011-10-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多