iosswiftswiftuiswiftcharts

Get widget accent color on bar marks for Swift Chart


I am currently trying to update my apps widgets to support iOS 18's tinted Home Screen customization option. For that I am trying to apply .widgetAccentable() where needed. However, I encounter an issue when it comes to Swift Charts.

My code looks currently something like this:

    Chart {
        ForEach(data, id: \.id) { day in
            BarMark(
                x: .value("###", day.date, unit: .day),
                y: .value("###", day.value)
            )
            .cornerRadius(10.0)
            .foregroundStyle(Color.blue.gradient)
            .alignsMarkStylesWithPlotArea()
            .annotation {
                Text(day.value.description)
                    .font(.subheadline)
            }
        }
    }
    .widgetAccentable()
    .chartYAxis(.hidden)
    .chartXAxis {
        AxisMarks(values: .stride(by: .day)) { value in
            AxisValueLabel(format: Date.FormatStyle().weekday(.short), centered: true)
        }
    }

The problem is that I can only apply .widgetAccentable() to the whole chart. This will tint everything (bar marks, labels, axis, ...) in the tint color.

What I want to achieve is to tint only the bar marks but not the labels and everything else ... those should stay in black/white monochrome colors.

Is there any way to make that work?


Solution

  • widgetAccentable doesn't support charts yet. Its documentation explains how it works,

    When applying the colors, the system treats the widget’s views as if they were template images. It ignores the view’s color — rendering the new colors based on the view’s alpha channel.

    So unless it becomes a modifier of ChartContent, you cannot just put the bars into the accented group.


    A workaround is to build an exact copy of the chart, with everything but the bars being transparent. Then put widgetAccentable on the overlay.

    var body: some View {
        ZStack {
            chart(accented: false)
            chart(accented: true)
        }
    }
    
    func chart(accented: Bool) -> some View {
        Chart {
            ForEach(data) { day in
                BarMark(
                    x: .value("###", day.date, unit: .day),
                    y: .value("###", day.value)
                )
                .cornerRadius(10.0)
                .alignsMarkStylesWithPlotArea()
                .annotation {
                    Text(day.value.description)
                        .font(.subheadline)
                        .opacity(accented ? 0 : 1)
                }
            }
        }
        .chartYAxis(.hidden)
        .chartXAxis {
            AxisMarks(values: .stride(by: .day)) { value in
                AxisValueLabel(format: Date.FormatStyle().weekday(.short), centered: true)
                    .foregroundStyle(accented ? .clear : .black)
            }
        }
        .widgetAccentable(accented)
    }