Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animatable transition of a chart foregroundStyle color property

Is possible to animate the color transition between two colors defined to a LineMark on a SwiftUI Chart?

I would like to see gradual change and not an abrupt switch between the colors, as you can see in this sample code:

struct SwiftUIViewTest: View {

    @State private var numbers = Array((0...100))

    @State private var color = true

    var body: some View {
        VStack{
            Button("Change Color") {
                color.toggle()
            }
            .buttonStyle(.borderedProminent)
            .padding()

            Chart(numbers, id: \.self) { number in
                LineMark(
                    x: .value("Index", number),
                    y: .value("Number", number)
                )
                //Property to animate with a gradual transition
                .foregroundStyle(color ? Color.blue : Color.yellow)
            }
            .animation(.default, value: color)
        }
        .padding(.horizontal)
    }
}

Thanks and best regards.

like image 924
João Colaço Avatar asked Oct 17 '25 07:10

João Colaço


1 Answers

If the change in line color cannot be animated using the usual techniques then I was thinking it might at least be possible to use an AnimatableModifier. Unfortunately, this doesn't work either, because LineMark does not have a .modifier function.

A non-abrupt color change can however be achieved by switching between two Chart renderings.

EDIT My earlier answer used an opacity transition between two chart renderings, these being the before-and-after versions of the chart when the color change is applied. This works fine for the color itself, but if other changes are applied at the same time, such as a change to the values, then only one of the two charts is animated. Specifically, the chart fading out is not animated (although you probably need to slow down the animation to notice it).

Here is a better way to do it. This time, both versions of the chart are drawn at all times, but the opacity modifier controls the one you see. Changes to other properties, such as the values, are now fully animated:

struct SwiftUIViewTest: View {

    static let straightLine = Array((0...10))
    static let zigZagLine = [0, 4, 2, 8, 3, 9, 6, 1, 7, 5, 10]

    @State private var numbers = SwiftUIViewTest.straightLine
    @State private var color = true

    private func theChart(foregroundColor: Color) -> some View {
        Chart(Array(numbers.enumerated()), id: \.element) { index, number in
            LineMark(
                x: .value("Index", index),
                y: .value("Number", number)
            )
            .foregroundStyle(foregroundColor)
        }
    }

    var body: some View {
        VStack{
            Button("Change Color and Values") {
                withAnimation {
                    color.toggle()
                    numbers = color ? SwiftUIViewTest.straightLine : SwiftUIViewTest.zigZagLine
                }
            }
            .buttonStyle(.borderedProminent)
            .padding()

            ZStack {
                theChart(foregroundColor: .blue)
                    .opacity(color ? 1 : 0)
                theChart(foregroundColor: .yellow)
                    .opacity(color ? 0 : 1)
            }
        }
        .padding(.horizontal)
    }
}

For reference, here is the earlier solution (pre-edit):

    ZStack {
        Chart(numbers, id: \.self) { number in
            // as before
        }
        .id(color)
    }

or alternatively:

    ZStack {
        if color {
            theChart
        } else {
            theChart
        }
    }
like image 58
Benzy Neez Avatar answered Oct 19 '25 08:10

Benzy Neez