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

如何在Charts中创建仅显示有数据日期的连续日期柱状图

SwiftUI Charts 实现仅显示有数据日期的连续柱状图

当训练会话数据存在日期缺失时(如下示例),SwiftUI Charts默认会按时间轴填充无数据的日期位置,导致柱状图出现空白。

// 示例缺失日期的训练数据
TrainingSession(date: formatter.date(from: "2024-05-12 07:37:30 +0000")!, maxRM: 10.0, totalVolume: 0.0),
TrainingSession(date: formatter.date(from: "2024-06-01 15:00:00 +0000")!, maxRM: 10.5, totalVolume: 105.0),
TrainingSession(date: formatter.date(from: "2024-06-03 15:00:00 +0000")!, maxRM: 10.0, totalVolume: 100.0)

要实现仅展示有数据日期的连续柱状图,核心是把X轴从时间轴类型改为类别类型,让Charts只根据现有数据生成轴标记。

修改方案

  1. 新增日期格式化逻辑,将Date转换为固定格式的字符串(如yyyy-MM-dd)
  2. 修改Chart的X轴绑定为日期字符串(类别值),而非原始Date类型
  3. 调整X轴标记,直接使用数据中的所有日期字符串,避免自动填充空白

修改后的完整代码

import SwiftUI
import Charts

struct TrainingSession {
    var date: Date
    var maxRM: Double
    var totalVolume: Double
    
    // 新增:格式化日期字符串,作为类别标识
    var formattedDate: String {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter.string(from: date)
    }
}

struct GraphView: View {
    var sessions: [TrainingSession]
    
    // 提取所有有数据的日期字符串
    private var sessionDates: [String] {
        sessions.map { $0.formattedDate }
    }

    var body: some View {
        ScrollView {
            VStack(alignment: .leading) {
                // 最大RM的图表
                VStack(alignment: .leading) {
                    Text("最大RM")
                        .font(.headline)
                        .padding()
                    Chart(sessions, id: \.formattedDate) { session in
                        BarMark(
                            // 修改X轴为类别值(日期字符串)
                            x: .value("日期", session.formattedDate),
                            y: .value("Max RM", session.maxRM)
                        )
                    }
                    .chartXAxis {
                        // 使用现有数据的日期生成轴标记,不自动填充空白
                        AxisMarks(values: sessionDates) { value in
                            AxisGridLine()
                            AxisTick()
                            AxisLabel(content: {
                                Text(value.as(String.self)!)
                                    .rotationEffect(.degrees(45)) // 旋转标签避免重叠
                            })
                        }
                    }
                    .chartScrollableAxes(.horizontal)
                    .padding([.leading, .trailing, .bottom])
                }

                // 总负荷量的图表
                VStack(alignment: .leading) {
                    Text("总负荷量")
                        .font(.headline)
                        .padding()
                    Chart(sessions, id: \.formattedDate) { session in
                        BarMark(
                            x: .value("日期", session.formattedDate),
                            y: .value("总负荷量", session.totalVolume)
                        )
                    }
                    .chartXAxis {
                        AxisMarks(values: sessionDates) { value in
                            AxisGridLine()
                            AxisTick()
                            AxisLabel(content: {
                                Text(value.as(String.self)!)
                                    .rotationEffect(.degrees(45))
                            })
                        }
                    }
                    .chartScrollableAxes(.horizontal)
                    .padding([.leading, .trailing, .bottom])
                }
            }
        }
    }
}

struct ContentView: View {
    var body: some View {
        GraphView(sessions: sampleData)
    }

    var sampleData: [TrainingSession] {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
        return [
            TrainingSession(date: formatter.date(from: "2024-05-12 07:37:30 +0000")!, maxRM: 10.0, totalVolume: 0.0),
            TrainingSession(date: formatter.date(from: "2024-06-01 15:00:00 +0000")!, maxRM: 10.5, totalVolume: 105.0),
            TrainingSession(date: formatter.date(from: "2024-06-03 15:00:00 +0000")!, maxRM: 10.0, totalVolume: 100.0)
        ]
    }
}

struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

关键修改点说明

  • 在TrainingSession中新增formattedDate计算属性,将日期转换为固定格式的字符串,作为类别标识
  • 把BarMark的x参数从.value("Date", session.date)改为.value("日期", session.formattedDate),将X轴从时间轴转为类别轴
  • chartXAxis部分使用sessionDates(现有数据的日期字符串数组)生成轴标记,避免Charts自动填充无数据的日期间隔
  • 对X轴标签添加旋转效果,防止日期字符串过长导致重叠

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 17:44:56