2

I'm trying to implement a way to detect hovering gesture on Chart in SwiftUI.

At first I tried adding .onHover() modifier to BarMark like below, but it seems it doesn't work because BarMark does not conform to View Protocol while .onHover() is defined View.

Chart(modules) { module in
          BarMark(x: .value("name", module.name), y: .value("Score", module.score)).onHover { over in
             details = module.name
     } // gives error
}

So does that mean If I want to show additional data when user hover over the Chart graph, then I have to create my own Chart graph rather than using Chart from Charts framework?

Cristik
  • 30,989
  • 25
  • 91
  • 127
Seungjun
  • 874
  • 9
  • 21

1 Answers1

2

You could try this approach, using an .chartOverlay and .onContinuousHover. You will have to adjust the location calculations to suit your purpose.

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"
    @State var isHovering = false
    
    var body: some View {
        Chart {
            ForEach(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())
                        .onContinuousHover { phase in
                            switch phase {
                            case .active(let location):
                                bar(at: location, proxy: proxy, geometry: geometry)
                                isHovering = true
                            case .ended:
                                isHovering = false
                            }
                        }
                }
            }
        }
    }
    
    func bar(at location: CGPoint, proxy: ChartProxy, geometry: GeometryProxy) {
        let xPosition = location.x - geometry[proxy.plotAreaFrame].origin.x
        let yPosition = location.y - geometry[proxy.plotAreaFrame].origin.y
        guard let month: String = proxy.value(atX: xPosition) else { return }
        guard let measure: Double = proxy.value(atY: yPosition) else { return }
        // more logic here ....
        select = month
    }
}

struct Measurement: Identifiable {
    var id: String
    var val: Double
}

If you want to just tap on the BarMarks, see my other answer at: How to change the color of BarView in SwiftUI charts when we tap on it