I am updating an old app written in Objective C to SwiftUI. One thing I am struggling with is how to style the axes with SwiftUI Charts. The old app used CorePlot.
This is the CorePlot version:
And the SwiftUI version:
I cannot figure out how to code in SwiftUI to get the same result as in the old version:
This is my code so far:
Chart(data) {
LineMark(
x: .value("x", $0.x),
y: .value("y", $0.y)
)
.interpolationMethod(.catmullRom)
.lineStyle(StrokeStyle(lineWidth: 1))
}
.chartXAxis {
AxisMarks(position: .bottom, values: .automatic) { _ in
AxisValueLabel(anchor: .top)
}
}
.chartXAxisLabel(position: .bottom, alignment: .center) {
Text(chartProperties.xTitle)
}
.chartXScale(domain: [chartProperties.minX, chartProperties.maxX])
.chartYAxisLabel(position: .leading, alignment: .center) {
Text(chartProperties.yTitle)
}
.chartYScale(domain: [chartProperties.minY, chartProperties.maxY])
.chartYAxis {
AxisMarks(position: .leading)
}
chartProperties
is a struct that has info such as axis titles and min and max values.
You can add the axis ticks simply using AxisTick
, and the axis lines can be added with AxisGridLine
. .foregroundStyle
can be added to both of these.
The grid lines "through" the chart that you see are also AxisGridLine
s added by SwiftUI by default. The defaults will not be added if you use the AxisMarks
initialiser that takes a @AxisMarkBuilder
closure.
In total, you need 3 sets of AxisMarks
for each axis:
Additionally, the tick marks should be offsetted, so that the grid lines go though the ticks.
Here is a complete example, where the y axis is (-2, 2), and x axis is (0, 400).
struct Foo: Identifiable {
let id: Double
let y: Double
}
let data: [Foo] = (0..<400).map { Foo(id: Double($0), y: log2(1 - Double.random(in: 0..<1)) / -10 - 1) }
struct ContentView: View {
var body: some View {
Chart(data) {
LineMark(
x: .value("x", $0.id),
y: .value("y", $0.y)
)
.interpolationMethod(.catmullRom)
.lineStyle(StrokeStyle(lineWidth: 1))
}
.chartXAxisLabel(position: .bottom, alignment: .center) {
Text("X Axis")
}
.chartXScale(domain: [0, 400])
.chartXAxis {
AxisMarks(position: .bottom, values: .stride(by: 100)) {
AxisValueLabel(anchor: .top)
}
AxisMarks(position: .bottom, values: .stride(by: 20)) { value in
if value.index != 0 { // the leftmost value does not need a tick
AxisTick(length: 8, stroke: .init(lineWidth: 1))
.offset(y: -4)
}
}
AxisMarks(position: .bottom, values: [0]) {
AxisGridLine(stroke: .init(lineWidth: 1))
}
}
.chartYAxisLabel(position: .leading, alignment: .center) {
Text("Y Label")
}
.chartYScale(domain: [-2, 2])
.chartYAxis {
AxisMarks(position: .leading, values: .stride(by: 1)) {
AxisValueLabel()
}
AxisMarks(position: .leading, values: .stride(by: 0.2)) { value in
if value.index != 0 { // the bottommost value does not need a tick
AxisTick(length: 8, stroke: .init(lineWidth: 1))
.offset(x: 4)
}
}
AxisMarks(position: .leading, values: [-2]) {
AxisGridLine(stroke: .init(lineWidth: 1))
}
}
.padding()
.aspectRatio(1, contentMode: .fit)
}
}