You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS Charts 3.1.1:X轴日期不准确、数据绘制异常求助

解决Charts框架展示体重数据时X轴异常与数据丢失问题

我之前也遇到过类似的Charts X轴刻度不匹配的问题,核心原因是你直接用时间戳作为X轴的数值,而Charts会根据数值范围自动生成均匀分布的刻度——你的时间跨度从2010到2018,范围极大,导致框架生成了大量中间刻度,同时乱序的时间戳也可能让图表绘制时丢失部分数据。下面是具体的解决方案:

问题根源拆解

  1. 时间戳数值过大:Charts会自动计算X轴刻度间隔,大跨度的时间戳会生成数百个中间刻度,导致你的原始日期被覆盖。
  2. 数据未排序:你的数据点x值(时间戳)是乱序的(2010→2018→2010→2018),折线图绘制时可能出现逻辑错误,导致部分点不显示。
  3. Formatter逻辑错误:当前的ChartFormatter只是将自动生成的刻度值转成日期,并没有关联你提供的原始日期数组。

分步解决方案

1. 预处理数据:排序并封装成模型

首先把分散的体重和日期数据封装成模型,再按日期排序,确保数据是时间有序的:

// 定义体重数据模型
struct WeightRecord {
    let weight: Double
    let displayDate: String // 提前格式化好的日期字符串,比如"MM/dd"
}

// 解析并排序数据(假设你已经拿到progressWeights数组)
var sortedWeightRecords = [WeightRecord]()
for rawData in progressWeights {
    // 解析体重
    let weight = Double(rawData["weight"] as! String)!
    // 解析UTC日期并格式化为展示用字符串
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
    dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
    let date = dateFormatter.date(from: rawData["createdOn"] as! String)!
    
    let displayFormatter = DateFormatter()
    displayFormatter.dateFormat = "MM/dd"
    let displayDate = displayFormatter.string(from: date)
    
    sortedWeightRecords.append(WeightRecord(weight: weight, displayDate: displayDate))
}

// 按日期排序(确保折线按时间顺序绘制)
sortedWeightRecords.sort { 
    // 这里可以直接比较日期,或者如果你需要的话也可以重新解析日期做排序
    let formatter = DateFormatter()
    formatter.dateFormat = "MM/dd"
    return formatter.date(from: $0.displayDate)! < formatter.date(from: $1.displayDate)!
}

2. 修改图表初始化:用索引替代时间戳作为X轴值

不再用时间戳作为X轴的数值,而是用数据的数组索引(0、1、2、3),这样Charts的X轴范围会被限定在0-3,不会生成多余刻度:

func setLineChart(records: [WeightRecord]) {
    var dataEntries = [ChartDataEntry]()
    
    // 用索引作为X轴值,确保每个数据点对应唯一的X刻度
    for (index, record) in records.enumerated() {
        let entry = ChartDataEntry(x: Double(index), y: record.weight)
        dataEntries.append(entry)
    }
    
    // 配置数据集
    let dataSet = LineChartDataSet(values: dataEntries, label: "Weight")
    let chartData = LineChartData(dataSet: dataSet)
    
    // 配置X轴
    let xAxis = lineChartView.xAxis
    let formatter = ChartFormatter()
    formatter.dateStrings = records.map { $0.displayDate } // 传入排序后的日期数组
    
    xAxis.valueFormatter = formatter
    xAxis.labelPosition = .bottom
    xAxis.drawGridLinesEnabled = false
    xAxis.labelCount = records.count // 强制显示所有标签
    xAxis.forceLabelsEnabled = true // 禁用自动合并标签
    xAxis.labelRotationAngle = -45 // 防止标签重叠
    
    lineChartView.data = chartData
}

3. 重写X轴Formatter:根据索引返回对应日期

修改ChartFormatter,让它根据X轴的索引值返回对应的原始日期:

public class ChartFormatter: NSObject, IAxisValueFormatter {
    var dateStrings = [String]() // 存储排序后的日期字符串
    
    public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
        let index = Int(value)
        // 边界检查,避免数组越界
        guard index >= 0 && index < dateStrings.count else {
            return ""
        }
        return dateStrings[index]
    }
}

为什么这样修改有效?

  • 用索引作为X轴值,把X轴范围压缩到0-3,Charts不会再生成多余的中间刻度,stringForValue只会被调用4次,完美匹配你的4个数据点。
  • 提前排序数据,确保折线按时间顺序绘制,避免因乱序导致的点丢失或折线交叉问题。
  • Formatter直接关联你的原始日期数组,每个X轴标签都和对应的数据点一一对应。

内容的提问来源于stack exchange,提问作者Jumpman987

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:08:25