in a more complex chart that displays multiple charts I want to add text annotations to the maximum and minimum value with the value for clarity. To make the annotations easy to read, I want to add a background and a border. When I started working with the annotations, the chart disappeared.
Eventually I discovered a few bugs or ugly features in Charts.
The problem is in the .overlay position of the annotations.
When the annotation is just Text(), everything is OK and the chart is displayed.
When you append .background to the Text() annotation at the .overlay position, the chart does not display.
.annotation(position: .overlay, alignment: .center) {
Text("\(hour.value)°")
.background(.yellow)
}
Full example:
struct ContentView: View {
struct Value: Identifiable {
var id = UUID()
var time: Date
var value: Int
var timeStr : String {
"\(time)"
}
}
let data = [
Value(time: Date().startOfHour().adding(hours: 0), value: 7),
Value(time: Date().startOfHour().adding(hours: 1), value: 9),
Value(time: Date().startOfHour().adding(hours: 2), value: 12),
Value(time: Date().startOfHour().adding(hours: 3), value: 16),
Value(time: Date().startOfHour().adding(hours: 4), value: 6),
]
var body: some View {
Chart (data) { hour in
LineMark(x: .value("Day", hour.time), y: .value("Value", hour.value))
.interpolationMethod(.catmullRom)
PointMark(x: .value("Day", hour.time), y: .value("Value", hour.value))
// step 1: change .top to .overlay and the graph disappears
.annotation(position: .top, alignment: .center) {
Text("\(hour.value)°")
// step 2: comment out all text attributes and the graph will reappear
.font(.caption2)
.padding(.horizontal, 3)
.background(
RoundedRectangle(cornerRadius: 4).fill(Color.blue.opacity(0.2))
)
.overlay(
RoundedRectangle(cornerRadius: 4)
.stroke(.blue, lineWidth: 1)
)
// step 2 end
}
}
.padding()
.frame(height: 200)
}
}
extension Date {
func startOfHour() -> Date {
let calendar = Calendar.current
var components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: self)
components.minute = 0
components.second = 0
return calendar.date(from: components)!
}
func adding(hours: Int) -> Date {
Calendar.current.date(byAdding: .hour, value: hours, to: self)!
}
}