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

SwiftUI Charts:如何将垂直条形图的Y轴标签居中于每个条形?

SwiftUI Charts垂直条形图Y轴日期标签居中实现方案

问题场景

使用新版SwiftUI Charts绘制垂直条形图时,Y轴的日期标签默认对齐在条形底部。尝试AxisValueLabel(centered: true)会导致标签错位、第一行标签缺失,AxisMarks(preset: .aligned)也无法解决问题,需要将Y轴日期标签居中于对应条形。

解决方案

核心思路是调整Y轴刻度的计算逻辑,让标签对应每个条形的垂直中心位置。以下提供两种可行方案:

方案一:保留日期类型的精准对齐

通过计算每个条形的垂直中心时间作为刻度锚点,同时还原原始日期标签,保证日期类型的可用性:

import SwiftUI
import Charts

struct ChartEntry: Identifiable {
    var id = UUID()
    let labelDate: Date
    let amount: Int
}

struct ContentView: View {
    let chartContent: [ChartEntry]
    
    // 计算每个日期对应的条形中心时间(原日期加30分钟)
    private var centeredYValues: [Date] {
        chartContent.map { entry in
            Calendar.current.date(byAdding: .minute, value: 30, to: entry.labelDate)!
        }
    }
    
    var body: some View {
        Chart {
            ForEach(chartContent) { entry in
                BarMark(
                    x: .value("数值", entry.amount),
                    y: .value("小时标签", entry.labelDate, unit: .hour)
                )
            }
        }
        .chartYAxis {
            AxisMarks(position: .leading, values: centeredYValues) { data in
                // 还原为原始日期,保证标签显示正确
                let originalDate = Calendar.current.date(byAdding: .minute, value: -30, to: data.value)!
                AxisValueLabel {
                    Text(originalDate.formatted(date: .omitted, time: .shortened))
                }
                // 调整网格线回到条形底部位置,保持视觉一致性
                AxisGridLine()
                    .position(.leading)
                    .offset(y: -15)
            }
        }
        .chartXScale(domain: 0...50)
    }
}

方案二:转为分类轴快速实现

将日期转为格式化字符串作为分类值,让图表自动将标签居中于条形,代码更简洁:

import SwiftUI
import Charts

struct ChartEntry: Identifiable {
    var id = UUID()
    let labelDate: Date
    let amount: Int
}

struct ContentView: View {
    let chartContent: [ChartEntry]
    
    var body: some View {
        Chart {
            ForEach(chartContent) { entry in
                BarMark(
                    x: .value("数值", entry.amount),
                    // 将日期转为字符串作为分类值
                    y: .value("小时标签", entry.labelDate.formatted(date: .omitted, time: .shortened))
                )
            }
        }
        .chartYAxis {
            AxisMarks(position: .leading) { data in
                AxisValueLabel()
            }
        }
        .chartXScale(domain: 0...50)
    }
}

方案对比

  • 方案一保留了日期类型,适合需要后续基于日期做逻辑计算的场景。
  • 方案二更轻量化,适合仅需展示日期字符串、无需保留日期类型的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 14:37:19