动态快速图表颜色图例

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

我对 SwiftUI 比较陌生,对 Swift Charts 完全陌生,我似乎碰壁了。我试图让我的图表反映

entryObservation.subject.color
中存在的颜色,但如果我使用简单的
.foregroundStyle(entryObservation.subject.color)
那么图例不会显示,即使我用
.chartLegend(.visible)
明确告诉它,并且如果我使用
.foregroundStyle(by: .value("Subject", entryObservation.subject.name))
那么图表不会反映我数据中的颜色,但图例会显示。

我做了一些挖掘,看来我可能需要使用

chartForegroundStyleScale(mapping:)
,但我很不知道如何做到这一点。请记住,数据是动态的,代码需要支持这一点,因此
.chartForegroundStyleScale(["Men": .blue, "Boys": .red, "Girls": .teal, "Women": .mint])
不起作用。 这是否是正确的路线,或者我应该构建一个自定义图例? 任何帮助将不胜感激, 乔什

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]
    @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", entryObservations.filter { $0.sectionGroup.name == entryObservation.sectionGroup.name }.count))
                        .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", entryObservations.filter { $0.subject.name == entryObservation.subject.name }.count))
                        .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()
.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)
}
swiftui swiftui-charts
1个回答
0
投票

我首先会创建一个像这样的

[String: Color]
,将主题名称映射到各自的颜色:

@State private var colors: [String: Color] = [:]

您可以在

onChange
中设置:

// this assumes that each name only corresponds to one color
// there must not be two subjects with the same name but different colours
.onChange(of: entryObservations, initial: true) { _, newValue in
    colors = Dictionary(uniqueKeysWithValues: Set(newValue.map(\.subject)).map { ($0.name, $0.color) })
}

然后您可以使用带有函数参数的

chartForegroundStyleScale
,使用
colors
字典将每个名称映射到颜色。

.chartForegroundStyleScale { (name: String) in
    colors[name] ?? .clear
}

我建议对两个不同的类别做类似的事情。创建两个

ChartData
数组,其中
ChartData
包含每个
BarMark
所需的所有数据,并更新
onChange
中的数组。这样您就不需要在每次视图更新时
filter

© www.soinside.com 2019 - 2024. All rights reserved.