SwiftUI Charts组合Area与Line Mark时颜色异常问题求助
问题描述
尝试创建包含两条折线的Line Chart,每条折线下方使用渐变颜色填充区域。当仅存在单条折线或折线与区域标记颜色相同时功能正常,但为折线和区域标记设置不同颜色时出现异常,分离不同Area系列也无法解决问题。
代码片段
Chart(data.chartData) { series in ForEach(Array(series.chartData), id: \.self) { element in LineMark( x: .value("Date", element.date), y: .value("Performance", element.value) ) .lineStyle(StrokeStyle(lineWidth: 1)) .foregroundStyle(by: .value("Name", series.name)) } if series.type == .type1 { AreaMark( x: .value("Date", element.date), y: .value("Performance", element.value) ) .foregroundStyle(areaGradient) } else if series.type == .index { AreaMark( x: .value("Date", element.date), y: .value("Performance", element.value) ) .foregroundStyle(areaIndexGradient) } } .chartForegroundStyleScale(range: [.green, .orange])
完整可运行示例
import SwiftUI import Charts struct PData: Codable, Hashable { var name: String var chartData: [Performance] } struct Performance: Codable, Hashable, Identifiable { var id: UUID = UUID() var name: String var type: SeriesType var chartData: [PerformanceValue] } struct PerformanceValue: Codable, Hashable, Identifiable { var id: UUID = UUID() var date: Date var value: Double } enum SeriesType: Codable { case type1, index } struct Example: View { let areaGradient = LinearGradient( gradient: Gradient ( colors: [ Color.green.opacity(0.5), Color.green.opacity(0.2), Color.green.opacity(0.05), Color.green.opacity(0.00) ] ), startPoint: .top, endPoint: .bottom ) let areaIndexGradient = LinearGradient( gradient: Gradient ( colors: [ Color.orange.opacity(0.5), Color.orange.opacity(0.2), Color.orange.opacity(0.05), Color.orange.opacity(0.00) ] ), startPoint: .top, endPoint: .bottom ) var body: some View { let series1 = Performance(name: "Series 1", type: .index, chartData: generatePerformanceValues()) let series2 = Performance(name: "Series 2", type: .type1, chartData: generatePerformanceValues()) let data = PData(name: "Chart Date", chartData: [series2, series1]) VStack { Chart(data.chartData) { series in ForEach(series.chartData) { element in LineMark( x: .value("Date", element.date), y: .value("Performance", element.value) ) .foregroundStyle(by: .value("Name", series.name)) if series.type == .type1 { AreaMark( x: .value("Date", element.date), y: .value("Performance", element.value) ) .foregroundStyle(areaGradient) } else if series.type == .index { AreaMark( x: .value("Date", element.date), y: .value("Performance", element.value) ) .foregroundStyle(areaIndexGradient) } } } .chartForegroundStyleScale(range: [.green, .orange]) } .padding() } func generatePerformanceValues() -> [PerformanceValue] { var performanceValues: [PerformanceValue] = [] var value = 3.0 // Starting value for month in 0..<50 { let date = Calendar.current.date(byAdding: .month, value: month, to: Date())! // Add a small random increment or decrement to the value let change = Double.random(in: -0.2...0.2) value += change // Clamp the value between 1 and 5 value = min(max(value, 1), 5) let performanceValue = PerformanceValue(date: date, value: value.rounded(toPlaces: 2)) performanceValues.append(performanceValue) } return performanceValues } } extension Double { func rounded(toPlaces places: Int) -> Double { let divisor = pow(10.0, Double(places)) return (self * divisor).rounded() / divisor } }
解决方案
问题核心在于:原代码按单个数据点循环生成标记,Chart无法正确识别系列关联;同时全局样式chartForegroundStyleScale会覆盖AreaMark的手动渐变设置,导致样式冲突。
修正后的代码
import SwiftUI import Charts // 数据结构保持不变 struct PData: Codable, Hashable { var name: String var chartData: [Performance] } struct Performance: Codable, Hashable, Identifiable { var id: UUID = UUID() var name: String var type: SeriesType var chartData: [PerformanceValue] } struct PerformanceValue: Codable, Hashable, Identifiable { var id: UUID = UUID() var date: Date var value: Double } enum SeriesType: Codable { case type1, index } struct Example: View { let areaGradient = LinearGradient( gradient: Gradient ( colors: [ Color.green.opacity(0.5), Color.green.opacity(0.2), Color.green.opacity(0.05), Color.green.opacity(0.00) ] ), startPoint: .top, endPoint: .bottom ) let areaIndexGradient = LinearGradient( gradient: Gradient ( colors: [ Color.orange.opacity(0.5), Color.orange.opacity(0.2), Color.orange.opacity(0.05), Color.orange.opacity(0.00) ] ), startPoint: .top, endPoint: .bottom ) var body: some View { let series1 = Performance(name: "Series 1", type: .index, chartData: generatePerformanceValues()) let series2 = Performance(name: "Series 2", type: .type1, chartData: generatePerformanceValues()) let data = PData(name: "Chart Date", chartData: [series2, series1]) VStack { Chart(data.chartData) { series in // 为整个系列创建折线标记 LineMark( x: .value("Date", series.chartData.map(\.date)), y: .value("Performance", series.chartData.map(\.value)) ) .lineStyle(StrokeStyle(lineWidth: 2)) // 手动指定折线颜色 .foregroundStyle(series.type == .type1 ? .green : .orange) // 为整个系列创建区域填充标记 AreaMark( x: .value("Date", series.chartData.map(\.date)), y: .value("Performance", series.chartData.map(\.value)) ) // 绑定对应渐变样式 .foregroundStyle(series.type == .type1 ? areaGradient : areaIndexGradient) } // 手动映射系列名称与颜色,确保图例显示正确 .chartForegroundStyleScale([ "Series 1": .orange, "Series 2": .green ]) .chartLegend(position: .bottom) } .padding() } func generatePerformanceValues() -> [PerformanceValue] { var performanceValues: [PerformanceValue] = [] var value = 3.0 for month in 0..<50 { let date = Calendar.current.date(byAdding: .month, value: month, to: Date())! let change = Double.random(in: -0.2...0.2) value += change value = min(max(value, 1), 5) let performanceValue = PerformanceValue(date: date, value: value.rounded(toPlaces: 2)) performanceValues.append(performanceValue) } return performanceValues } } extension Double { func rounded(toPlaces places: Int) -> Double { let divisor = pow(10.0, Double(places)) return (self * divisor).rounded() / divisor } }
关键修正点
- 按系列组织标记:直接为每个
series创建LineMark和AreaMark,使用整个系列的数据集,让Chart正确识别系列关联,避免单个数据点标记的混乱。 - 分离样式控制:手动为折线指定颜色,为区域填充设置独立渐变,避免全局样式覆盖自定义渐变。
- 明确图例映射:通过
chartForegroundStyleScale手动绑定系列名称与颜色,保证图例显示和实际样式一致。
内容的提问来源于stack exchange,提问作者DaVinci
相关产品推荐
相关产品推荐

