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

SwiftUI Charts:基于时间绘制日期(让DateComponents遵循Plottable)

问题描述

希望基于ISO 8601格式的日期item.sunrise,分别提取日期和时间维度绘制图表,但编译器报错:

Initializer init(x:y:) requires that DateComponents conform to Plottable

相关代码如下:

Chart{
    ForEach(sunriseSunsetDates) {item in
        LineMark(
            x: .value("Date", calendar.dateComponents([.year, .month, .day], from: item.sunrise)),
            y: .value("Time", calendar.dateComponents([.hour, .minute, .second], from: item.sunrise))
        )
        .foregroundStyle(.blue.gradient)
        .interpolationMethod(.catmullRom)
    }
}

请问该需求能否实现,还是需要改用其他写法?


解决方案

这个需求完全可以实现,问题核心是DateComponents并没有遵循SwiftUI Charts要求的Plottable协议,因此不能直接作为坐标轴值传入LineMark。你可以通过两种常见方式修复:

方式一:将日期/时间转换为Date类型

把提取的日期、时间维度重新封装为Date实例(Date天然支持Plottable),同时通过坐标轴格式化控制显示样式:

let calendar = Calendar.current

Chart{
    ForEach(sunriseSunsetDates) { item in
        // 提取日期部分,转为仅包含年月日的Date
        guard let dateOnly = calendar.date(from: calendar.dateComponents([.year, .month, .day], from: item.sunrise)) else { return }
        
        // 提取时间部分,基于一个固定基准日期生成仅包含时分秒的Date
        let timeComponents = calendar.dateComponents([.hour, .minute, .second], from: item.sunrise)
        guard let timeOnly = calendar.date(bySettingHour: timeComponents.hour!, minute: timeComponents.minute!, second: timeComponents.second!, of: Date.distantPast) else { return }
        
        LineMark(
            x: .value("Date", dateOnly),
            y: .value("Time", timeOnly)
        )
        .foregroundStyle(.blue.gradient)
        .interpolationMethod(.catmullRom)
    }
}
// 自定义X轴显示格式:仅展示年月日
.chartXAxis {
    AxisMarks(position: .bottom) { _ in
        AxisGridLine()
        AxisTick()
        AxisLabel(format: .dateTime.day().month().year())
    }
}
// 自定义Y轴显示格式:仅展示时分
.chartYAxis {
    AxisMarks(position: .leading) { _ in
        AxisGridLine()
        AxisTick()
        AxisLabel(format: .dateTime.hour().minute())
    }
}

方式二:将时间转换为数值类型(秒数)

如果不需要Y轴以时间格式展示,也可以把时间转换为当天0点到日出时刻的秒数(数值类型天然支持Plottable),实现起来更简洁:

let calendar = Calendar.current

Chart{
    ForEach(sunriseSunsetDates) { item in
        guard let dateOnly = calendar.date(from: calendar.dateComponents([.year, .month, .day], from: item.sunrise)) else { return }
        // 计算当天0点的时间
        let startOfDay = calendar.startOfDay(for: item.sunrise)
        // 计算日出时刻与当天0点的时间差(秒)
        let timeInSeconds = item.sunrise.timeIntervalSince(startOfDay)
        
        LineMark(
            x: .value("Date", dateOnly),
            y: .value("Time (Seconds)", timeInSeconds)
        )
        .foregroundStyle(.blue.gradient)
        .interpolationMethod(.catmullRom)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 08:45:24