iosswiftuiswiftcharts

Is there a way to put RuleMark on SwiftUI Chart to the back of BarMark?


I need to show a line (average for eg.) behind single bar chart.

I use RuleMark to create a line but it always on top of bar chart. Is there a way to put the red dash line behind the blue bars ?

struct ContentView: View {
    var body: some View {
        debugChartAvgLine
    }

    var debugChartAvgLine: some View {
        let salesData: [(String, Int)] = [
            ("Jan", 100),
            ("Feb", 150),
            ("Mar", 100),
            ("Apr", 50),
            ("May", 70),
            ("June", 140)
        ]
        let avg = salesData.map{$0.1}.reduce(0, +) / salesData.count
        
        return Chart {
            ForEach(salesData, id: \.0) {  month, sales in
                BarMark(
                    x: .value("Month", month),
                    y: .value("Amount", sales)
                )
            }
            RuleMark(y: .value("avg sale", avg))
                .lineStyle(StrokeStyle(lineWidth: 1, dash: [5]))
                .foregroundStyle(.red)
        }
        .frame(height: 300)
    }
}

screenshot


Solution

  • zIndex is available for any ChartContent. By default it is 0, so you can use something like -1 to put the RuleMark behind the bars.

    Chart {
        ForEach(salesData, id: \.0) {  month, sales in
            BarMark(
                x: .value("Month", month),
                y: .value("Amount", sales)
            )
        }
        RuleMark(y: .value("avg sale", avg))
            .lineStyle(StrokeStyle(lineWidth: 1, dash: [5]))
            .foregroundStyle(.red)
            .zIndex(-1)
    }
    .frame(height: 300)
    

    Simply putting the RuleMark before the BarMarks seems to work too.

    Chart {
        RuleMark(y: .value("avg sale", avg))
            .lineStyle(StrokeStyle(lineWidth: 1, dash: [5]))
            .foregroundStyle(.red)
        ForEach(salesData, id: \.0) {  month, sales in
            BarMark(
                x: .value("Month", month),
                y: .value("Amount", sales)
            )
        }
    }
    .frame(height: 300)