peter flanagan
peter flanagan

Reputation: 9790

SwiftUI charts - only show RuleMark below plotted line

I am having some issues with Swift charts. I have the following Chart which has RuleMark. As you can see from the screenshot below the RuleMark extends the length of the screen. I am attempting to only show the mark below the line. Something like this question is asking. If any further clarity is required let me know. The code is below.

enter image description here

struct Item: Identifiable {
    var date: String
    var count: Double
    var id = UUID()
}

var data: [Item] = [
    .init(date: "10AM", count: 5),
    .init(date: "12AM", count: 4),
    .init(date: "2PM", count: 7),
    .init(date: "4PM", count: 2),
    .init(date: "6PM", count: 9)
]

struct ContentView: View {
    var body: some View {
        Chart(data) { item in    
            LineMark(
                x: .value("Date", item.date),
                y: .value("Value", item.count)
            )
            
            PointMark(
                x: .value("Date", item.date),
                y: .value("Value", item.count)
            )
            
            RuleMark(
                x: .value("Value", item.date)
            )
        }
        .chartYAxis(.hidden)
    }
}

Upvotes: 1

Views: 654

Answers (1)

Sweeper
Sweeper

Reputation: 270770

Assuming the y axis value of the bottom of the plot is known, you can do:

RuleMark(
    x: .value("Date", item.date),
    yStart: .value("Bottom", 0), // assuming y=0 at the bottom of the chart
    yEnd: .value("Value", item.count)
)

Replace 0 with whatever value the y axis has at the very bottom. Note that you can change the y axis' range using the various chartYScale modifiers.

If the y value of the bottom of the plot is not known (e.g. you are letting SwiftUI automatically decide what scale to use depending on the data), you can use chartBackground/chartOverlay to draw the lines with a Path:

.chartBackground { proxy in
    if let frameAnchor = proxy.plotFrame {
        GeometryReader { geo in
            let frame = geo[frameAnchor]
            ForEach(data) { item in
                if let point = proxy.position(for: (x: item.date, y: item.count)) {
                    Path { p in
                        p.move(to: .init(x: point.x + frame.minX, y: point.y + frame.minY))
                        p.addLine(to: .init(x: point.x + frame.minX, y: frame.maxY))
                    }
                    .stroke(.blue, lineWidth: 1)
                }
            }
        }
    }
}

Upvotes: 3

Related Questions