I'm creating a chart in SwiftUI. It's weather related and I have the data successfully displaying. Now, I'm attempting to display the temperature on each symbol in the chart, but it only displays on the first symbol.
Here's what I've done:
Chart {
ForEach(temperatures, id: \.tempType) { series in
ForEach(series.data) { element in
LineMark(x: .value("Day", element.day), y: .value("Temps", element.temp))
.interpolationMethod(.catmullRom)
.foregroundStyle(by: .value("TemperatureType", series.tempType))
.symbol {
Circle()
.fill(.yellow)
.frame(width: 10)
.shadow(radius: 2)
}
.lineStyle(.init(lineWidth: 5))
} // end for each
} // end for each
}
This works. Then, I attempt to add text using this modifier on the LineMark:
.annotation(position: .overlay, alignment: .bottom) {
let text = "\(element.temp)"
Text(text)
}
It only displays the text on the first symbol's data point:
Since the annotation modifier is within the ForEach loop, I thought it would display the temperature at each data point, but it doesn't. What's the best way to have the temperature displayed at each symbol instead of only the first?
The short answer is that the .annotation
refers to the type of "Mark" that you attach it to - and you are attaching it to a LineMark
, so it is the entire line you are "annotating", not the individual points.
Had you used BarMark
s or PointMark
s, the annotation will attach to the individual bar or point. So try this instead:
Chart {
ForEach(Array(zip(numbers, numbers.indices)), id: \.0) { number, index in
LineMark(
x: .value("Index", index),
y: .value("Value", number)
)
.lineStyle(.init(lineWidth: 5))
PointMark(
x: .value("Index", index),
y: .value("Value", number)
)
.annotation(position: .overlay,
alignment: .bottom,
spacing: 10) {
Text("\(number)")
.font(.largeTitle)
}
}
}
To make it compatible with your nice symbols, we need to add a couple of extra steps:
Chart {
ForEach(Array(zip(numbers, numbers.indices)), id: \.0) { number, index in
LineMark(
x: .value("Index", index),
y: .value("Value", number)
)
.interpolationMethod(.catmullRom)
.lineStyle(.init(lineWidth: 5))
.symbol {
// This still needs to be associated
// with the LineMark
Circle()
.fill(.yellow)
.frame(width: 10)
.shadow(radius: 2)
}
PointMark(
x: .value("Index", index),
y: .value("Value", number)
)
// We need .opacity(0) or it will
// overlay your `.symbol`
.opacity(0)
.annotation(position: .overlay,
alignment: .bottom,
spacing: 10) {
Text("\(number)")
.font(.largeTitle)
}
}
}