Swift Chart动态颜色图例问题:自定义颜色与图例显示冲突
解决Swift Charts自定义颜色与图例共存问题
你找的chartForegroundStyleScale是正确方向,不用自定义图例,只需动态生成Subject名称到颜色的映射即可。
核心思路
- 直接设置
.foregroundStyle(entryObservation.subject.color)时,Charts无法识别这是分组样式,因此不会生成图例 - 使用
.foregroundStyle(by: .value("Subject", ...))能让Charts识别分组并生成图例,配合chartForegroundStyleScale可以替换默认颜色为你的自定义色值
实现步骤
- 从传入的
entryObservations中提取唯一的Subject集合,避免重复项 - 将集合转换为
[String: Color]格式的映射字典,键是Subject名称,值是对应的自定义颜色 - 把这个字典传给Chart的
chartForegroundStyleScale(mapping:)修饰符
修改后的完整代码
import SwiftUI import Charts struct EntryObservation { var subject: Subject var sectionGroup: SectionGroup var time: Date } struct Subject { var name: String var color: Color } struct SectionGroup { var name: String } struct ClickerView: View { var entryObservations: [EntryObservation] // 动态生成Subject名称到颜色的映射 private var subjectColorMap: [String: Color] { // 提取唯一的Subject,避免重复 let uniqueSubjects = Array(Set(entryObservations.map { $0.subject })) return Dictionary(uniqueKeysWithValues: uniqueSubjects.map { ($0.name, $0.color) }) } @State private var categorization = 1 var body: some View { Chart (entryObservations, id: \.time) {entryObservation in Plot { switch categorization { case 2: BarMark(x: .value("Section", entryObservation.sectionGroup.name), y: .value("Count", 1)) .foregroundStyle(by: .value("Subject", entryObservation.subject.name)) .cornerRadius(10) .annotation(position: .top) { Text("\(entryObservations.filter { $0.sectionGroup.name == entryObservation.sectionGroup.name }.count)") .foregroundColor(Color.gray) .font(.system(size: 12, weight: .bold)) } default: BarMark(x: .value("Subject", entryObservation.subject.name), y: .value("Count", 1)) .cornerRadius(10) .foregroundStyle(by: .value("Subject", entryObservation.subject.name)) .annotation(position: .top) { Text("\(entryObservations.filter { $0.subject.name == entryObservation.subject.name }.count)") .foregroundColor(Color.gray) .font(.system(size: 12, weight: .bold)) } } } } .padding() // 应用自定义颜色映射 .chartForegroundStyleScale(mapping: subjectColorMap) .chartLegend(.visible) .chartYAxisLabel("Count") .safeAreaInset(edge: .top) { VStack { Picker("Categorisation", selection: $categorization) { Text("Subject").tag(1) Text("Section").tag(2) } .pickerStyle(.segmented) } } } } let entryObservations: [EntryObservation] = [ EntryObservation(subject: Subject(name: "Men", color: .blue), sectionGroup: SectionGroup(name: "School"), time: Date(timeIntervalSince1970: 1000)), EntryObservation(subject: Subject(name: "Men", color: .blue), sectionGroup: SectionGroup(name: "School"), time: Date(timeIntervalSince1970: 1300)), EntryObservation(subject: Subject(name: "Men", color: .blue), sectionGroup: SectionGroup(name: "Mall"), time: Date(timeIntervalSince1970: 2000)), EntryObservation(subject: Subject(name: "Boys", color: .red), sectionGroup: SectionGroup(name: "School"), time: Date(timeIntervalSince1970: 1900)), EntryObservation(subject: Subject(name: "Boys", color: .red), sectionGroup: SectionGroup(name: "Mall"), time: Date(timeIntervalSince1970: 2200)), EntryObservation(subject: Subject(name: "Boys", color: .red), sectionGroup: SectionGroup(name: "School"), time: Date(timeIntervalSince1970: 2500)), EntryObservation(subject: Subject(name: "Girls", color: .teal), sectionGroup: SectionGroup(name: "Mall"), time: Date(timeIntervalSince1970: 2800)), EntryObservation(subject: Subject(name: "Girls", color: .teal), sectionGroup: SectionGroup(name: "School"), time: Date(timeIntervalSince1970: 3100)), EntryObservation(subject: Subject(name: "Women", color: .mint), sectionGroup: SectionGroup(name: "Mall"), time: Date(timeIntervalSince1970: 3400)), EntryObservation(subject: Subject(name: "Women", color: .mint), sectionGroup: SectionGroup(name: "Mall"), time: Date(timeIntervalSince1970: 3700)), EntryObservation(subject: Subject(name: "Women", color: .mint), sectionGroup: SectionGroup(name: "School"), time: Date(timeIntervalSince1970: 4000)), EntryObservation(subject: Subject(name: "Women", color: .mint), sectionGroup: SectionGroup(name: "Mall"), time: Date(timeIntervalSince1970: 4300)), EntryObservation(subject: Subject(name: "Women", color: .mint), sectionGroup: SectionGroup(name: "School"), time: Date(timeIntervalSince1970: 4600)), EntryObservation(subject: Subject(name: "Women", color: .mint), sectionGroup: SectionGroup(name: "Mall"), time: Date(timeIntervalSince1970: 4900)), EntryObservation(subject: Subject(name: "Women", color: .mint), sectionGroup: SectionGroup(name: "School"), time: Date(timeIntervalSince1970: 5200)) ] #Preview { ClickerView(entryObservations: entryObservations) }
补充说明
- 使用
Set提取唯一Subject是因为原数据中可能存在重复的Subject实例,确保映射字典里每个名称只对应一个颜色 - 保持
.foregroundStyle(by: .value("Subject", ...))的写法,Charts会根据这个分组信息自动生成匹配颜色的图例
内容的提问来源于stack exchange,提问作者The-Wolf
相关产品推荐
相关产品推荐

