SwiftUI中如何检测BarMark悬停手势?是否需自定义Chart?
在SwiftUI Charts中实现鼠标悬停展示额外数据的方案
不用放弃官方Charts框架,通过以下方案就能实现悬停检测并展示额外数据:
方案一:利用chartOverlay + ChartProxy检测悬停位置
通过给Chart添加.chartOverlay修饰符,在图表上层放置透明视图接收hover事件,再借助ChartProxy获取鼠标位置对应的数据点,最后用annotation展示额外信息。
示例代码:
import SwiftUI import Charts struct Module: Identifiable { let id = UUID() let name: String let score: Double } struct ChartHoverView: View { @State private var modules: [Module] = [ .init(name: "数学", score: 85), .init(name: "英语", score: 92), .init(name: "物理", score: 78) ] @State private var hoveredModuleName: String? var body: some View { Chart(modules) { module in BarMark( x: .value("科目", module.name), y: .value("分数", module.score) ) // 悬停时显示分数标注 .annotation(position: .top) { if hoveredModuleName == module.name { Text("\(module.score)分") .font(.caption) .padding(4) .background(Color.white) .cornerRadius(4) .shadow(radius: 2) } } } .chartOverlay { proxy in GeometryReader { geometry in Rectangle() .fill(Color.clear) .onHover { isHovering in guard isHovering else { hoveredModuleName = nil return } // 转换鼠标位置到图表坐标系 let mouseLocation = NSEvent.mouseLocation let chartCoordinate = geometry.convert(mouseLocation, from: .screen) // 获取鼠标对应的X轴值(科目名称) if let targetName = proxy.value(atX: chartCoordinate.x) as? String { hoveredModuleName = targetName } } } } .padding() } }
方案二:配合chartSymbolHoverEffect添加视觉反馈(iOS17+/macOS14+)
如果系统版本满足要求,还可以用.chartSymbolHoverEffect给BarMark添加悬停时的视觉效果(比如缩放、背景高亮),进一步提升交互体验。
示例代码:
Chart(modules) { module in BarMark( x: .value("科目", module.name), y: .value("分数", module.score) ) // 悬停时的视觉效果 .chartSymbolHoverEffect { symbol in symbol .scaleEffect(1.05) .background(Color.blue.opacity(0.2)) .cornerRadius(4) } .annotation(position: .top) { if hoveredModuleName == module.name { Text("\(module.score)分") .padding(6) .background(Color.white) .cornerRadius(6) .shadow(radius: 3) } } } .chartOverlay { proxy in GeometryReader { geo in Color.clear .onHover { over in guard over else { hoveredModuleName = nil return } let point = geo.approximateMouseLocation if let targetName = proxy.value(atX: point.x) as? String { hoveredModuleName = targetName } } } }
核心原理
BarMark确实不遵循View协议,无法直接添加.onHover,但我们可以通过Chart的.chartOverlay修饰符,在整个图表上层覆盖一个可接收手势的透明视图,再利用ChartProxy将屏幕坐标映射到图表的数据维度,从而找到当前悬停的目标数据项,最终实现展示额外信息的需求。
内容的提问来源于stack exchange,提问作者Seungjun
相关产品推荐
相关产品推荐

