X轴上重复数值的iOS图表

3
我正在使用iOS Charts,我的x轴上出现了重复的标签。
enter image description here 我得到了许多答案(first, second, third),它们都建议我设置粒度设置:
xAxis.granularityEnabled = true
xAxis.granularity = 1.0

但是,当我设置x轴标签时,它们会简单地消失。

enter image description here

我的 x 轴设置:

let xAxis = lineChartView.xAxis
xAxis.labelPosition = .bottom
xAxis.labelFont = .boldSystemFont(ofSize: 12)
xAxis.setLabelCount(6, force: false)
xAxis.labelTextColor = UIColor(red: 0, green: 0, blue: 255/255, alpha: 1.0)
xAxis.axisLineColor = UIColor(white: 0.2, alpha: 0.4)
xAxis.gridColor = UIColor(white: 0.8, alpha: 0.4)
xAxis.valueFormatter = ChartXAxisFormatter()

我根据文档的建议,创建了一个自定义类来格式化传递给x轴的值:

class ChartXAxisFormatter: IAxisValueFormatter {
    func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        let dateFormatter = DateFormatter()
        dateFormatter.setLocalizedDateFormatFromTemplate("MM/dd")
        let date = Date(timeIntervalSince1970: value)
        return dateFormatter.string(from: date)
    }
}

更新

感谢 @aiwiguna 和 Charts repo 的帮助,我成功创建了一个自定义类,该类接收两个参数:日期(Double)和相应的索引(Int),而不仅仅是日期。

class ChartXAxisFormatter: NSObject, IAxisValueFormatter {
    private var dates: [Double]?
    
    convenience init(usingDates dateArr: [Double]) {
        self.init()
        self.dates = dateArr
    }
    
    func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        print("value: \(value)")
        print("dates: \(dates)")
        let index = Int(value)
    
        guard let dates = dates, index < dates.count else {
            return "?"
        }
        
        let date = dates[index]
        let dateFormatter = DateFormatter()
        dateFormatter.setLocalizedDateFormatFromTemplate("MM/dd")
        let formattedDate = Date(timeIntervalSince1970: date.rounded())
        return dateFormatter.string(from: formattedDate)
    }
}

这个想法是创建一个日期数组[1598409060.0, 1598409060.0],通过访问索引一个一个地返回它们。记录valueindex可以证明这一点。问题在于,虽然重复标签的数量已经减少到与绘图数量匹配,但重复项仍然存在。

enter image description here

这两个图应该在同一X轴标签“8/25”上堆叠。
我正在使用枚举()来提取ChartDataEntry的索引:
var dateArr = [Double]()
for (index, fetchedMetric) in fetchedMetrics.enumerated() {
    let date = fetchedMetric.date.timeIntervalSince1970
    dateArr.append(date)
    if var yValues = metricDict[fetchedMetric.unit] {
        yValues.append(ChartDataEntry(x: Double(index), y: Double(truncating: fetchedMetric.value)))
        metricDict.updateValue(yValues, forKey: fetchedMetric.unit)
    } else {
        metricDict.updateValue([ChartDataEntry(x: Double(index), y: Double(truncating: fetchedMetric.value))], forKey: fetchedMetric.unit)
    }
}
2个回答

2

这是因为当您设置图表设置(granurality = 1和ChartXAxisFormatter)时,它会在xAxis中显示每一秒。

我更喜欢使用数组日期来显示轴上的日期,类似于这样的方式:

public class DateValueFormatter: NSObject, IAxisValueFormatter {
    private let dateFormatter = DateFormatter()
    private let dates:[Date]
    
    init(dates: [Date]) {
        self.dates= dates
        super.init()
        dateFormatter.dateFormat = "dd MMM HH:mm"
    }
    
    public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        if value >= 0 && value < objects.count{
            let date = dates[Int(value)]
            return dateFormatter.string(from: date)
        }   
        return ""
    }
}

如果您不想更改格式化后的值,可以尝试将粒度设置为86400(1天的总秒数)


@Kevvv,你需要确保同一天的x值相同。 - aiwiguna
1
在最近的 Charts 版本中,值是自 1970 年以来的时间间隔,这使得该解决方案不起作用。有关如何处理此问题的任何建议? - James Selvakumar

0
为了避免接收到重复的值,在这种情况下,我们必须创建一个新的变量minimalTimeInterval,在我的例子中是24小时,并始终记录先前插入的值previousValue
更改前的图表: 更改前的图表 更改后的图表: 更改后的图表 代码:
public class DateValueFormatter: NSObject, AxisValueFormatter {
    private let dateFormatter = DateFormatter()
    private var previousValue: Double = 0
    private let minimalTimeInterval: Double
    
    init(dateFormatter: String, minimalTimeInterval: Double = 60 * 60 * 24) {
        self.dateFormatter.calendar = Calendar(identifier: .gregorian)
        self.dateFormatter.dateFormat = dateFormatter //"dd MMM (E)"
        self.minimalTimeInterval = minimalTimeInterval
        super.init()
    }
    
    public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        let delta = value - previousValue
        if(delta >= minimalTimeInterval || delta < 0) {
            previousValue = value
            return dateFormatter.string(from: Date(timeIntervalSince1970: value))
        }
        return ""
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接