1

I'am trying to handle click on annotation in SwiftUI 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..

Apple provides some code for click handle, but I don't know how to use it for strings (Apple has Date in example) and don't have compare bars like me.

Any ideas please?

enter image description here

mfaani
  • 33,269
  • 19
  • 164
  • 293
ankmara
  • 265
  • 3
  • 13

1 Answers1

6

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!")
      }
   }
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Flightfire
  • 61
  • 1
  • 4