I am trying to create a tap gesture on an annotation in SwiftUI's BarMark:
var body: some View {
List {
Chart {
ForEach(data) {
BarMark(
x: .value("Mount", $0.mount),
y: .value("Value", $0.value)
)
.foregroundStyle(by: .value("Type", "Series \($0.type)"))
.position(by: .value("Type", $0.type))
.annotation {
HStack {
Rectangle()
.fill(Color.red.opacity(0.2))
.frame(width: 20, height: 20)
.clipShape(Circle())
.onTapGesture {
print("Tapped!") // Never called
}
}
}
}
}
.frame(height: 250)
.labelsHidden()
}
}
I also tried Button with action, Image etc.. But it seems like all interactions in annotation are disabled or I don't know what is needed.
Apple provides some code for handling a click, but I don't know how to use it for strings. Apple uses Date in their example and they don't have compare bars like me.
Any ideas, please?

It seems like Charts is designed to be just a flattened view without any interaction. If you want to interact with the elements of the Chart, you have to use
.chartOverlay()
This function overlays a view on the chart and then you have to use a GeometryReader in order to find the specific location interacted with.
Here is the example from the Apple documentation:
Chart(data) {
LineMark(
x: .value("date", $0.date),
y: .value("price", $0.price)
)
}
.chartOverlay { proxy in
GeometryReader { geometry in
Rectangle().fill(.clear).contentShape(Rectangle())
.gesture(
DragGesture()
.onChanged { value in
// Convert the gesture location to the coordiante space of the plot area.
let origin = geometry[proxy.plotAreaFrame].origin
let location = CGPoint(
x: value.location.x - origin.x,
y: value.location.y - origin.y
)
// Get the x (date) and y (price) value from the location.
let (date, price) = proxy.value(at: location, as: (Date, Double).self)
print("Location: \(date), \(price)")
}
)
}
}
So for your example it would be something like this:
Chart {
ForEach(data) {
BarMark(
x: .value("Mount", $0.mount),
y: .value("Value", $0.value)
)
}
}
.chartOverlay { proxy in
GeometryReader { geometry in
Rectangle().fill(.clear).contentShape(Rectangle())
.onTapGesture { location in
guard let value: (mount, value) = proxy.value(at: location) else {
return
}
//Check if value is included in the data from the chart
print("Tapped!")
}
}
}
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