Search code examples
swiftswiftuichartsswiftui-charts

BarMark Chart with +ve and -ve value in SwiftUI


Try to set -ve and +ve value and zero as Ycenter

Please check my code and my graph below. image below this is my graph

ForEach(0..<stack.count, id: \.self) { index in
                                            
    if let xValue = stack[index].x ,
        let yValue = stack[index].yValues?[0].value,
          let hexaString = stack[index].yValues?[0].color {

            BarMark(
                x: .value("x", "\(xValue)"),
                y: .value("y", "\(yValue)")
             )
           .foregroundStyle(Color(hex: hexaString))
         }
       }

but i want this type of graph see below I need this type of graph


Solution

  • You are plotting the y axes as categorical data (i.e. strings), which makes all the bars rectangles with the same size.

    If the x and y values are numerical, just plot them as numerical data.

    BarMark(
        x: .value("X", xValue),
        y: .value("Y", yValue),
        width: 10 // you should specify the width of the bar
    )
    

    To fix y=0 at the centre, you can use chartYScale to modify the range of the y axis.

    // dataType should be passed the type of yValue
    .chartYScale(domain: .automatic(includesZero: true, dataType: Double.self) { domain in
        // find the largest number in magnitude in the inferred domain
        if let max = domain.map(abs).max() {
            // make sure both the positive and negative of that is in the domain
            domain.append(max)
            domain.append(-max)
        }  
    })
    

    Here is a full example:

    struct Foo: Identifiable {
        let id = UUID()
        let x: Double
        let y: Double
    }
    
    // have some positive data
    let data = (0..<10).map { x in
        Foo(x: Double(x), y: Double.random(in: 0...100))
    // and some negative data
    } + (10..<20).map { x in
        Foo(x: Double(x), y: Double.random(in: -20..<0))
    }
    // the negative data only goes from -20..<0 to demonstrate that y=0 can be centred
    
    struct ContentView: View {
        var body: some View {
            Chart(data) { foo in
                BarMark(
                    x: .value("X", foo.x),
                    y: .value("Y", foo.y),
                    width: 10
                )
                .foregroundStyle(foo.y < 0 ? .red : .green)
            }
            .chartYScale(domain: .automatic(includesZero: true, dataType: Double.self) { domain in
                if let max = domain.map(abs).max() {
                    domain.append(max)
                    domain.append(-max)
                }
                
            })
            .frame(height: 500)
            .padding()
        }
    }
    

    Output:

    enter image description here