I am trying to reuse a SwiftUI Swift Chart view instead of having six separately defined Chart views. (The six separate charts are working fine)
Q: How does one use a var to represent different "columns" of data from my array of structs I want to be plotted.
(element.xGyro ➙ element.yGyro ) in the y: .value("xGyro", element.xGyro)
To reduce code duplication, I'd rather not use a switch/case statement for the whole Chart {} block.
I did try using a switch/case for just the y: .value... line but I got an error.
What I don't know how to do is replace the (currently) hard-coded element.xGyro with a variable that I can pass in to plot different points [ xGyro | yGyro | zGyro | xAccel | yAccel | zAccel ] in the following line with a variable.
y: .value("xGyro", element.xGyro)
The source for my charts is an array of structs MotionDataPointClass.swift:
class MotionDataPointM: Identifiable {
var xGyro: Double
var yGyro: Double
var zGyro: Double
var xAccel: Double
var yAccel: Double
var zAccel: Double
var timeStamp: Date
var myIndex: Int
var id: Date { timeStamp }
}
The chart code is SingleChartView.swift:
import os
import SwiftUI
import CoreMotion
import CoreLocation
import Charts
struct SingleChartView: View {
@ObservedObject var locationsHandler = LocationsHandler.shared
let motionDataPoints: [MotionDataPointM]
let numberOfPointsToPlot: Int
let vertScaleMin: Double
let vertScaleMax: Double
let cornerLabel: String
let lineColor: Color
let valuesToPlot: Int
var body: some View {
Chart{
ForEach(motionDataPoints.suffix(numberOfPointsToPlot)) { element in
LineMark(
x: .value("Date", element.timeStamp),
y: .value("xGyro", element.xGyro) // <-- HERE
)
}
.interpolationMethod(.catmullRom)
}
.foregroundStyle(lineColor)
.chartYScale(domain: [vertScaleMin, vertScaleMax])
.padding(.top, 10.0)
.padding(.bottom, 3.0)
.padding(.horizontal, 3.0)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.purple, lineWidth: 1.0)
)
.overlay(
HStack {
VStack {
Text("\(cornerLabel)")
.font(.largeTitle)
.fontWeight(.black)
.foregroundColor(lineColor)
.opacity(0.3)
.padding(.leading, 4)
.padding(.top, -3)
Spacer()
}
Spacer()
}
)
}
}
I am calling/invoking the chart from my ContentView.swift file:
SingleChartView(motionDataPoints: motionDataPoints, numberOfPointsToPlot: numberOfPointsToPlot, vertScaleMin: gyroMinX, vertScaleMax: gyroMaxX, cornerLabel: "W", lineColor: Color("YChartLines"), valuesToPlot: vectorToPlot.xGyro)
I defined an enum to represent the different columns/vectors in the data struct:
enum vectorToPlot {
public static let xGyro = 1
public static let yGyro = 2
public static let zGyro = 3
public static let xAccel = 4
public static let yAccel = 5
public static let zAccel = 6
}
The data you want to plot just so happens to all be
Doubles, so you can use aKeyPath<MotionDataPointM, Double>to represent the property you want to plot.Here is an example:
For some reason, key paths don't work well in things like the
selectionof aPicker, so you might want to keep your enum, and keep a mapping from your enum to theKeyPaths.If the properties you want to plot are not all the same type, you would need to a
switchto produceLineMarks, or you can makeSingleChartViewgeneric, if the property each chart plots is constant: