Swift UI PointMark 图标在 Y 轴上被切断,并在 SwiftUI Char 中显示日期数据

问题描述 投票:0回答:1

在 SwiftUI Chart 中显示 PointMark 的问题是,当我使用 PointMark 显示多个点图标时,并且 Y 轴代表 Date 类型数据,图标不完全可见。最上面的点图标被切掉了,一半不显示了。

运行结果

为了解决这个问题,我尝试通过使用 .chartYAxisRange(min: adjustmentMinValue, max: adjustmentMaxValue) 计算代码中的最小值和最大值来向 Y 轴添加范围,但它不断抛出错误:“编译器是无法在合理的时间内对该表达式进行类型检查;尝试将表达式分解为不同的子表达式,我注释掉了该部分代码。

如果有人能帮忙看看这个问题,我将不胜感激。

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
    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
//            let (minDate, maxDate) = lineChartViewData.getMinMaxDate()
            TabView(selection: $selectedWeek) {
                ForEach(0..<lineChartViewData.getWeeklyData().count, id: \.self) { weekIndex in
                    VStack{
                        Chart (lineChartViewData.getWeeklyData()[weekIndex]) { 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: minDate, max: maxDate)*/
                
                            
                        }
                    }.tag(weekIndex)
                }
            }.frame(height: 150).tabViewStyle(PageTabViewStyle())
                .onAppear {
                  
                    selectedWeek = lineChartViewData.getWeeklyData().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
    }
    
    func getMinMaxDate() -> (min: Date, max: Date) {
           let values = dataArray.map { convertFullDateToHMDate($0.detailDay) }
           let minValue = values.min() ?? Date()
           let maxValue = values.max() ?? Date()
           let padding: TimeInterval = 60 * 10
           let adjustedMinValue = minValue.addingTimeInterval(-padding)
           let adjustedMaxValue = maxValue.addingTimeInterval(padding)
           
           return (adjustedMinValue, adjustedMaxValue)
       }
}


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))
        
        
    }
}

ios swiftui charts
1个回答
0
投票

这是我的完整测试代码,非常适合我使用

.chartYScale(domain: ...)
。在真实设备(不是预览版)上测试, 使用 MacOS 15.2 和 XCode 16.2 的 iOS-18 和 MacCatalyst。

struct PointChartView: View {
    var lineChartViewData: LineChartViewData
    @State private var selectedWeek: Int = 0
    
    @State private var drange: (min: Date, max: Date) = (Date(), Date()) // <--- here

    var body: some View {
        VStack(alignment: .leading, spacing: 16) {
            TabView(selection: $selectedWeek) {
                ForEach(0..<lineChartViewData.getWeeklyData().count, id: \.self) { weekIndex in
                    VStack{
                        Chart (lineChartViewData.getWeeklyData()[weekIndex]) { 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)")
                                }
                            }
                        }
                        .chartYScale(domain: drange.min...drange.max) // <--- here
                    }.tag(weekIndex)
                }
            }
            .frame(height: 150).tabViewStyle(PageTabViewStyle())
            .onAppear {
                selectedWeek = lineChartViewData.getWeeklyData().count - 1
                drange = lineChartViewData.getMinMaxDate() // <--- here
            }
        }
        .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
    }
    
    func getMinMaxDate() -> (min: Date, max: Date) {
           let values = dataArray.map { convertFullDateToHMDate($0.detailDay) }
           let minValue = values.min() ?? Date()
           let maxValue = values.max() ?? Date()
           let padding: TimeInterval = 60 * 10
           let adjustedMinValue = minValue.addingTimeInterval(-padding)
           let adjustedMaxValue = maxValue.addingTimeInterval(padding)

           return (adjustedMinValue, adjustedMaxValue)
       }
}

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))
    }
}

struct ContentView: View {
    let temperatureData: [TemperatureData] = [
        TemperatureData(day: "9-9", detailDay: "2024-12-01 10:31:00"),
        TemperatureData(day: "8-8", detailDay: "2024-12-02 10:32:00"),
        TemperatureData(day: "7-7", detailDay: "2024-12-03 10:33:00"),
    ]
    
    var body: some View {
        PointChartView(lineChartViewData: LineChartViewData(imageName: "IconPoop" ,isArtificial: false, dataArray: temperatureData))
    }
}

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
}
© www.soinside.com 2019 - 2024. All rights reserved.