You try this approach, using a `chartOverlay` and a `select` variable to
change the bar color when the user tap on that bar.
struct ContentView: View {
let measurement: [Measurement] = [
Measurement(id: "1", val: 11.2),
Measurement(id: "2", val: 22.2),
Measurement(id: "3", val: 38.2)
]
@State var select = "0"
var body: some View {
Chart(measurement) { data in
BarMark(x: .value("Time", data.id), y: .value("val", data.val))
.foregroundStyle(select == data.id ? .blue : .red)
}
.chartOverlay { proxy in
GeometryReader { geometry in
ZStack(alignment: .top) {
Rectangle().fill(.clear).contentShape(Rectangle())
.onTapGesture { location in
doSelection(at: location, proxy: proxy, geometry: geometry)
}
}
}
}
}
func doSelection(at location: CGPoint, proxy: ChartProxy, geometry: GeometryProxy) {
let xPos = location.x - geometry[proxy.plotAreaFrame].origin.x
guard let xbar: String = proxy.value(atX: xPos) else { return }
select = xbar
}
}
struct Measurement: Identifiable {
var id: String
var val: Double
}
try this approach, using a `ForEach` loop and having the `RuleMark` after it:
var body: some View {
Chart { // <-- here
ForEach(viewModel.forecast) { data in // <-- here
BarMark(x: .value("Month", data.month), y: .value("Kr", data.cost))
.foregroundStyle(Color.blue)
} // <-- here
if let selectedMonth = viewModel.selectedMonth,
let selectedCost = viewModel.selectedCost {
RuleMark(x: .value("Selected month", selectedMonth))
.annotation(position: .top, alignment: .top) {
VStack {
Text(estimated(for: selectedMonth, and: selectedCost))
}
}
}
}
.chartYScale(domain: 0...100) // <-- here
.chartYAxis {
AxisMarks(position: .leading)
}
.chartOverlay { proxy in
GeometryReader { geometry in
ZStack(alignment: .top) {
Rectangle().fill(.clear).contentShape(Rectangle())
.onTapGesture { location in
updateSelectedMonth(at: location, proxy: proxy, geometry: geometry)
}
}
}
}
}