Here is an example showing two different data sets on the same axis. You need to scale the data yourself though, so in this example I've done so manually with the adjustments to the pressure values, but you could also do this programatically.
Example here at github.com/jknlsn/MultipleDataSetSwiftChartsExample with lollipop detail popover, and slightly simplified example below.
import Charts
import SwiftUI
import WeatherKit
struct HourWeatherStruct {
var date: Date
var pressure: Double
var temperature: Double
var windSpeed: Double
}
let hours: [HourWeatherStruct] = [
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600),
pressure: 1015.0,
temperature: 18.2,
windSpeed: 6.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 2),
pressure: 1015.3,
temperature: 18.2,
windSpeed: 8.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 3),
pressure: 1015.9,
temperature: 18.2,
windSpeed: 9.4),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 4),
pressure: 1016.3,
temperature: 18.2,
windSpeed: 5.2),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 5),
pressure: 1016.3,
temperature: 18.2,
windSpeed: 12.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 6),
pressure: 1016.3,
temperature: 18.2,
windSpeed: 11.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 7),
pressure: 1017.3,
temperature: 18.2,
windSpeed: 10.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 8),
pressure: 1018.3,
temperature: 18.2,
windSpeed: 11.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 9),
pressure: 1018.3,
temperature: 18.2,
windSpeed: 9.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 10),
pressure: 1018.3,
temperature: 18.2,
windSpeed: 8.1),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 11),
pressure: 1017.3,
temperature: 18.2,
windSpeed: 19.9),
HourWeatherStruct(date: Date(timeIntervalSinceNow: 3600 * 12),
pressure: 1018.3,
temperature: 18.2,
windSpeed: 7.1),
]
struct InteractiveLollipopChartMinimal: View {
var body: some View {
Chart {
ForEach(hours, id: \.date) {
LineMark(
x: .value("Date", $0.date, unit: .hour),
y: .value("Wind Speed", $0.windSpeed)
)
.foregroundStyle(by: .value("Value", "Wind"))
LineMark(
x: .value("Date", $0.date, unit: .hour),
y: .value("Pressure", ($0.pressure - 1014) * 4)
)
.foregroundStyle(by: .value("Value", "Pressure"))
}
.lineStyle(StrokeStyle(lineWidth: 4.0))
.interpolationMethod(.catmullRom)
}
.chartForegroundStyleScale([
"Pressure": .purple,
"Wind": .teal
])
.chartXAxis {
AxisMarks(position: .bottom, values: .stride(by: .hour, count: 2)) {
_ in
AxisTick()
AxisGridLine()
AxisValueLabel(format: .dateTime.hour(), centered: true)
}
}
.chartYAxis {
AxisMarks(position: .leading, values: Array(stride(from: 0, through: 24, by: 4))){
axis in
AxisTick()
AxisGridLine()
AxisValueLabel("\(1014 + (axis.index * 1))", centered: false)
}
AxisMarks(position: .trailing, values: Array(stride(from: 0, through: 24, by: 4))){
axis in
AxisTick()
AxisGridLine()
AxisValueLabel("\(axis.index * 4)", centered: false)
}
}
}
}
struct InteractiveLollipopMinimal: View {
var body: some View {
List {
VStack(alignment: .leading) {
VStack(alignment: .leading) {
Text("Windspeed and Pressure")
.font(.callout)
.foregroundStyle(.secondary)
Text("\(hours.first?.date ?? Date(), format: .dateTime)")
.font(.title2.bold())
}
InteractiveLollipopChartMinimal()
.frame(height: 200)
}
.listRowSeparator(.hidden)
}
.listStyle(.plain)
.navigationBarTitle("Interactive Lollipop", displayMode: .inline)
}
}