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.
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With