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

如何在SwiftUI Chart折线图中沿折线绘制渐变?

在SwiftUI Chart中实现沿折线路径的渐变

可行。SwiftUI Chart的LineMark默认渐变是基于图表整体坐标系的,要实现沿折线本身的渐变,需要通过自定义路径叠加的方式实现,具体方案如下:

实现思路

  1. 保留原Chart结构维持坐标轴和布局逻辑,将原LineMark设为透明;
  2. 通过GeometryReader结合图表刻度(Scale)把数据点转换为视图坐标;
  3. 创建自定义路径并应用与原折线一致的插值方法;
  4. 给路径添加沿起点到终点的线性渐变。

完整代码示例

struct GradientLineChartView: View {
    @ObservedObject var viewModel: ChartViewModel
    
    var body: some View {
        Chart {
            // 保留原LineMark用于维持图表布局,设为透明
            ForEach(viewModel.items) { shape in
                LineMark(
                    x: .value("Date", shape.createDay, unit: .day),
                    y: .value("Count", shape.value)
                )
                .foregroundStyle(.clear)
            }
            .interpolationMethod(.cardinal(tension: 3))
        }
        // 明确刻度定义域,确保坐标转换准确
        .chartXScale {
            LinearScale(domain: viewModel.items.map(\.createDay))
        }
        .chartYScale {
            LinearScale(domain: [0, (viewModel.items.map(\.value).max() ?? 0) * 1.1]) // 预留顶部空间
        }
        .overlay {
            GeometryReader { geo in
                // 获取图表的X、Y刻度转换器
                let xScale = geo[chartXScaleID: "Date"] as? LinearScale<Date>
                let yScale = geo[chartYScaleID: "Count"] as? LinearScale<Double>
                
                Path { path in
                    guard let firstItem = viewModel.items.first,
                          let firstX = xScale?.convert(firstItem.createDay),
                          let firstY = yScale?.convert(firstItem.value) else { return }
                    
                    path.move(to: CGPoint(x: firstX, y: firstY))
                    
                    // 连接所有数据点
                    for item in viewModel.items.dropFirst() {
                        guard let x = xScale?.convert(item.createDay),
                              let y = yScale?.convert(item.value) else { continue }
                        path.addLine(to: CGPoint(x: x, y: y))
                    }
                }
                .interpolate(.cardinal(tension: 3)) // 与原折线保持一致的平滑插值
                .stroke(
                    LinearGradient(
                        colors: [.accentColor, .clear],
                        startPoint: .start,
                        endPoint: .end
                    ),
                    style: StrokeStyle(lineWidth: 6, lineCap: .round)
                )
            }
        }
    }
}

// 数据模型与ViewModel示例
struct ChartItem: Identifiable {
    let id = UUID()
    let createDay: Date
    let value: Double
}

class ChartViewModel: ObservableObject {
    @Published var items: [ChartItem] = []
    
    init() {
        let calendar = Calendar.current
        let today = Date()
        items = (0..<7).map { day in
            ChartItem(
                createDay: calendar.date(byAdding: .day, value: -day, to: today)!,
                value: Double.random(in: 10...50)
            )
        }
    }
}

关键说明

  • 渐变的.start和.end会自动对应路径的起点和终点,因此渐变会完全沿着折线走向分布;
  • 明确chartXScale和chartYScale的定义域,能避免坐标转换出现偏差;
  • 原LineMark设为透明是为了让Chart自动计算合适的坐标轴范围,无需手动调整刻度区间。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 23:47:11