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

Swift组合图表层级顺序问题:Area Mark始终置顶

解决SwiftUI Chart中Mark层级覆盖问题

问题原因

SwiftUI Chart的Mark渲染层级是按类型优先级固定的:AreaMark > LineMark > PointMark,所以无论你在代码中调整Mark的顺序,AreaMark都会默认显示在最上层。而同类型Mark的层级则由它们在Chart中的生成顺序决定,后生成的会覆盖先生成的。

解决方案

通过ZStack叠加多个Chart来手动控制层级:

  1. 底层Chart仅渲染AreaMark
  2. 上层Chart渲染所有LineMark和PointMark,并调整同类型Mark的生成顺序来控制它们的层级

修改后的代码示例

var body: some View {
    ScrollView(.horizontal, showsIndicators: false) {
        ZStack {
            // 底层:仅渲染AreaMark
            Chart(data.filter { $0.owner == .global }) { mark in
                AreaMark(
                    x: .value("day", mark.day),
                    y: .value("score", mark.score)
                )
                .foregroundStyle(.indigo)
                .opacity(1)
                .interpolationMethod(.catmullRom)
            }
            .chartXScale(domain: dates)
            .chartYScale(domain: getYAxis())

            // 上层:渲染Line和Point Mark,控制顺序调整层级
            Chart(data) { mark in
                // Friends line - 先渲染,会被后面的Player Line覆盖
                if mark.owner == .friends {
                    LineMark(
                        x: .value("day", mark.day),
                        y: .value(mark.type == .score ? "score" : "seconds", mark.score),
                        series: .value("Friends Score", "friends")
                    )
                    .interpolationMethod(.catmullRom)
                    .foregroundStyle(.yellow)
                    .lineStyle(StrokeStyle(lineWidth: 3, dash: [5, 10]))
                }

                // Player line - 后渲染,显示在Friends Line上方
                if mark.owner == .player {
                    LineMark(
                        x: .value("day", mark.day),
                        y: .value("score", mark.score),
                        series: .value("Player Score", "player")
                    )
                    .interpolationMethod(.catmullRom)
                }

                // Friends annotation
                if mark.owner == .friends {
                    PointMark(
                        x: .value("day", mark.day),
                        y: .value("score", mark.score)
                    )
                    .interpolationMethod(.catmullRom)
                    .symbol() {
                        Circle()
                            .strokeBorder(.black)
                            .background(Circle().fill(.orange))
                            .frame(width: 10)
                    }
                    .annotation() {
                        Text("\(mark.score)")
                    }
                }

                // Player annotation - 后渲染,显示在Friends标注上方
                if mark.owner == .player {
                    PointMark(
                        x: .value("day", mark.day),
                        y: .value(dataPoint == .score ? "score" : "seconds", mark.score)
                    )
                    .symbol() {
                        Circle()
                            .strokeBorder(.black)
                            .background(Circle().fill(.blue))
                            .frame(width: 10)
                    }
                    .annotation() {
                        Text("\(mark.score)")
                    }
                    .interpolationMethod(.catmullRom)
                }
            }
            .chartXScale(domain: dates)
            .chartYScale(domain: getYAxis())
        }
        .frame(minWidth: screen.size.height*3, maxHeight: screen.size.height/3)
    }
}

说明

  • 用ZStack将两个Chart叠加,确保上层的Line/Point完全显示在Area之上
  • 在同一个上层Chart中,将需要显示在上方的Mark(比如Player Line和标注)放在后面生成,覆盖先渲染的Friends相关Mark
  • 两个Chart都设置相同的X/Y轴范围,保证对齐一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 23:34:56