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
}
}