You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 01:35:25