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

SwiftUI macOS应用:右键ContextMenu获取点击位置识别线条

解决方案:SwiftUI macOS 右键点击竖线唤起ContextMenu并识别对应线条

针对你的频谱展示场景需求,这里提供两种可行的解决方案,分别适配不同的使用场景:

方案一:为每条线条绑定独立的可点击区域(推荐)

问题根源:你之前用ForEach绘制单条线条时,contentShape(Rectangle())会把整个视图的矩形作为点击区域,导致所有线条的点击区域重叠,最后一条的区域覆盖了前面所有线条,因此点击任何位置都只会触发最后一条的菜单。

解决思路:为每条线条设置独立的垂直窄条作为可点击区域,宽度设为相邻线条间距的一半,确保点击区域不重叠且覆盖线条两侧的区域,这样点击X方向任意位置都会落到对应线条的可点击范围内。

ForEach(Array(legends.enumerated()), id: \.offset) { offset, _ in
    // 计算当前线条的X坐标
    let lineX = CGFloat(offset) * spacing * pixelPerHz
    // 可点击区域宽度:相邻线条间距的一半,确保覆盖线条两侧的点击范围
    let tapAreaWidth = spacing * pixelPerHz / 2
    // 可点击区域的起始X:当前线条X减去半宽,让区域居中于线条
    let tapAreaX = lineX - tapAreaWidth / 2
    
    Path { path in
        path.move(to: CGPoint(x: lineX, y: 0))
        path.addLine(to: CGPoint(x: lineX, y: height))
    }
    .stroke(color, lineWidth: 1)
    // 设置仅当前线条对应的垂直窄条为可点击区域
    .contentShape(
        Rectangle()
            .frame(width: tapAreaWidth, height: height)
            .offset(x: tapAreaX, y: 0)
    )
    .contextMenu {
        Button("Do It") {
            print("触发第 \(offset + 1) 条线的操作,X坐标:\(lineX)")
        }
    }
}

方案优势

  • 完全遵循SwiftUI声明式风格,逻辑清晰
  • 点击区域精准对应线条,无歧义
  • 无需额外手势处理,直接利用原生contextMenu特性

方案二:全局点击+计算对应线条(支持任意位置点击)

如果需要支持点击任意X位置(包括线条之间的空白区域),自动匹配到最近的线条,可以通过SpatialTapGesture获取点击位置,计算对应线条索引后手动唤起ContextMenu。

@State private var showContextMenu = false
@State private var selectedLineIndex = 0
@State private var selectedLineX: CGFloat = 0

var body: some View {
    ZStack {
        // 绘制所有竖线
        Path { path in
            var xPosition: CGFloat = (-low.truncatingRemainder(dividingBy: params.spacing)) * pixelPerHz
            repeat {
                path.move(to: CGPoint(x: xPosition, y: 0))
                path.addLine(to: CGPoint(x: xPosition, y: height))
                xPosition += pixelPerHz * params.spacing
            } while xPosition < width
        }
        .stroke(color, lineWidth: 1)
        
        // 透明覆盖层,接收所有右键点击
        Color.clear
            .contentShape(Rectangle())
            .gesture(
                SpatialTapGesture(count: 1, coordinateSpace: .local)
                    .triggeredBy(.secondary) // 响应macOS右键点击
                    .onEnded { event in
                        let tapX = event.location.x
                        let spacingPx = pixelPerHz * params.spacing
                        // 计算点击位置对应的最近线条索引(四舍五入)
                        selectedLineIndex = Int((tapX + spacingPx / 2) / spacingPx)
                        selectedLineX = CGFloat(selectedLineIndex) * spacingPx
                        showContextMenu = true
                    }
            )
            .contextMenu(isPresented: $showContextMenu) {
                Button("Do It") {
                    print("触发第 \(selectedLineIndex + 1) 条线的操作,X坐标:\(selectedLineX)")
                }
            }
    }
}

方案优势

  • 支持任意位置点击,自动匹配最近的线条
  • 无需为每条线条单独绑定菜单,适合线条数量极多的场景

内容的提问来源于stack exchange,提问作者DougPA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 16:07:07