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

SwiftUI Chart中Y轴为Date类型时PointMark图标被截断求助

问题解决:SwiftUI Chart中PointMark图标截断与编译器类型检查超时

问题描述

在SwiftUI Chart使用PointMark展示图标时,Y轴为Date类型数据时顶部图标会被截断(仅显示一半)。尝试通过计算最小/最大值并使用.chartYAxisRange(min: adjustedMinValue, max: adjustedMaxValue)设置Y轴范围时,编译器报错:

The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions

解决方案

1. 修复图标截断问题

图标截断的核心原因是Y轴范围刚好贴合数据的最小/最大时间,没有给图标预留显示空间。需要给Y轴范围添加上下时间padding(比如10分钟),让图标能完整显示。

2. 解决编译器类型检查超时问题

编译器报错是因为在View的body中直接进行复杂日期计算+范围设置,导致类型推断过载。需要把日期范围的计算拆分成独立子表达式,并且针对当前Tab的周数据计算对应范围(而非整个数据集的范围)。

修改后的完整代码

import Foundation
import SwiftUI
import Charts

func convertFullDateToHMDate(_ fullDateString: String) -> Date {
    let fullFormatter = DateFormatter()
    fullFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    let fullDate = fullFormatter.date(from: fullDateString)!
    let calendar = Calendar.current
    let components = calendar.dateComponents([.hour, .minute, .second], from: fullDate)
    return calendar.date(from: components)!
}

func formatDateToHMS(_ date: Date) -> String {
    let formatter = DateFormatter()
    formatter.dateFormat = "HH:mm:ss"
    return formatter.string(from: date)
}

struct TemperatureData: Identifiable, Codable {
    var id = UUID()
    var day: String
    var detailDay: String // detail time
    var temperature: Double = 0.0
}

struct PointChartView: View {
    var lineChartViewData: LineChartViewData
    @State private var selectedWeek: Int = 0
    
    // 提前缓存每周的数据及其对应的日期范围
    private var weeklyDataWithRange: [(data: [TemperatureData], minDate: Date, maxDate: Date)] {
        lineChartViewData.getWeeklyData().map { weekData in
            let dates = weekData.map { convertFullDateToHMDate($0.detailDay) }
            let minDate = dates.min() ?? Date()
            let maxDate = dates.max() ?? Date()
            // 添加10分钟的上下padding
            let padding: TimeInterval = 60 * 10
            let adjustedMin = minDate.addingTimeInterval(-padding)
            let adjustedMax = maxDate.addingTimeInterval(padding)
            return (weekData, adjustedMin, adjustedMax)
        }
    }
    
    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            TabView(selection: $selectedWeek) {
                ForEach(weeklyDataWithRange.indices, id: \.self) { index in
                    let weekItem = weeklyDataWithRange[index]
                    
                    VStack{
                        Chart(weekItem.data) { item in
                            PointMark(
                                x: .value("day", item.day),
                                y: .value("time", convertFullDateToHMDate(item.detailDay))
                            )
                            .symbol {
                                Image(systemName: "square.fill")
                                    .resizable()
                                    .frame(width: 15, height: 15)
                                    .foregroundStyle(Color.green)
                            }
                        }
                        .chartYAxis {
                            AxisMarks(position: .leading) { value in
                                if let dateValue = value.as(Date.self) {
                                    AxisTick()
                                    AxisGridLine()
                                    AxisValueLabel(formatDateToHMS(dateValue))
                                } else {
                                    AxisValueLabel("\(value.as(Double.self) ?? 0.0)")
                                }
                            }
                            // 直接使用提前计算好的范围,避免编译器类型推断压力
                            .chartYAxisRange(min: weekItem.minDate, max: weekItem.maxDate)
                        }
                    }
                    .tag(index)
                }
            }
            .frame(height: 150)
            .tabViewStyle(PageTabViewStyle())
            .onAppear {
                selectedWeek = weeklyDataWithRange.count - 1
            }
        }
        .padding(16)
        .background(
            ZStack {
                if lineChartViewData.isArtificial {
                    Rectangle()
                        .fill(Color.gray.opacity(0.5))
                        .cornerRadius(30)
                        .background(.ultraThinMaterial)
                } else {
                    Rectangle()
                        .fill(Color.gray.opacity(0))
                        .background(.ultraThinMaterial)
                        .cornerRadius(30)
                }
            }
        )
    }
}

struct LineChartViewData: Identifiable, Codable {
    var id = UUID()
    var imageName: String
    var isArtificial: Bool
    var dataArray: [TemperatureData] = []
    
    func getWeeklyData() -> [[TemperatureData]] {
        var twoDimensionalArray: [[TemperatureData]] = []
        var subarray: [TemperatureData] = []
        for element in self.dataArray {
            subarray.append(element)
            if subarray.count == 7 {
                twoDimensionalArray.append(subarray)
                subarray = []
            }
        }
        if !subarray.isEmpty {
            twoDimensionalArray.append(subarray)
        }
        return twoDimensionalArray
    }
}

struct PointChartView_Previews: PreviewProvider {
    static var previews: some View {
        let temperatureData: [TemperatureData] = [
            TemperatureData(day: "9-9", detailDay: "2024-12-01 10:31:00"),
            TemperatureData(day: "9-9", detailDay: "2024-12-01 10:32:00"),
            TemperatureData(day: "9-9", detailDay: "2024-12-01 10:33:00"),
        ]
        PointChartView(lineChartViewData: LineChartViewData(imageName: "IconPoop", isArtificial: false, dataArray: temperatureData))
    }
}

关键修改点

  • 添加weeklyDataWithRange计算属性,提前缓存每周数据的日期范围(包含上下padding),把复杂计算从body中剥离,避免编译器类型检查压力。
  • 针对每个Tab的周数据单独计算日期范围,而非使用整个数据集的范围,确保每个图表的Y轴适配当前数据。
  • 在.chartYAxisRange中直接使用提前计算好的minDate和maxDate,让编译器能轻松推断类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 20:40:02