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

Swift Chart动态颜色图例问题:自定义颜色与图例显示冲突

解决Swift Charts自定义颜色与图例共存问题

你找的chartForegroundStyleScale是正确方向,不用自定义图例,只需动态生成Subject名称到颜色的映射即可。

核心思路

  • 直接设置.foregroundStyle(entryObservation.subject.color)时,Charts无法识别这是分组样式,因此不会生成图例
  • 使用.foregroundStyle(by: .value("Subject", ...))能让Charts识别分组并生成图例,配合chartForegroundStyleScale可以替换默认颜色为你的自定义色值

实现步骤

  1. 从传入的entryObservations中提取唯一的Subject集合,避免重复项
  2. 将集合转换为[String: Color]格式的映射字典,键是Subject名称,值是对应的自定义颜色
  3. 把这个字典传给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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 04:08:15