I have read How to add Strings on X Axis in iOS-charts? but it does not answer my question.
I have rendered a line chart with iOS-charts with no x/y axis labels, only datapoint labels, like this. This chart shows the temperatureValues in the chart's ChartDataEntry as the Y at intervals of 0..
var temperaturevalues: [Double] = [47.0, 48.0, 49.0, 50.0, 51.0, 50.0, 49.0]
However, I want to add an unrelated array of times to the datapoint labels, with times[i] matching temperatureValues at i.
var times: [String] = ["7 AM", "8 AM", "11 AM", "2 PM", "5 PM", "8 PM", "11 PM"]
I have extended IValueFormatter in a separate class like this:
class ChartsFormatterToDegrees: IValueFormatter {
func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String {
return "\(Int(value))°"
}
but it only allows me to customize the dataPoint label using the value in the ChartDataEntry which is the temperatureValue being used to map out Y in the lineChart. How can I add another array at indexValue so that the dataPoints labels appear like this instead?
return """
\(Int(value))°
\(timesOfDay)
"""
Here is my charts method:
private func setupChartView(temperatureValues: [Double], timesDataPoints: [String]) {
var tempDataEntries: [ChartDataEntry] = []
let chartsFormatterToDegrees = ChartsFormatterToDegrees(time: timeDataPoints)
for eachTemp in 0..<temperatureValues.count {
let tempEntry = ChartDataEntry(x: Double(eachTemp), y: temperatureValues[eachTemp])
tempDataEntries.append(tempEntry)
}
let lineChartDataSet = LineChartDataSet(entries: tempDataEntries, label: "nil")
lineChartDataSet.valueFormatter = chartsFormatterToDegrees
let chartData = LineChartData(dataSets: [lineChartDataSet])
chartData.addDataSet(lineChartDataSet)
lineChartView.data = chartData
}
As I best understand it, the IValueFormatter extension only lets you modify the values being used to draw the chart, not add additional string arrays at index. When I tried the below, it only prints the timesOfDay at the dataSetIndex, it only prints timesOfDay[0]
on all the dataPoint labels.
class ChartsFormatterToDegrees: IValueFormatter {
init(time: [String]) {
timesOfDay = time
}
var timesOfDay = [String]()
func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String {
return """
\(Int(value))°
\(timesOfDay[dataSetIndex])
"""
}
}
Here's my most updated method with print:
class ChartsFormatterToDegrees: IValueFormatter {
var timesOfDay = [String]()
var values = [Double]()
init(values: [Double], time: [String]) {
timesOfDay = time
//print(values, "are the values") //prints values correctly
}
func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String {
//print(value, "at", dataSetIndex) //prints as 37.0 at 0, 39.0 at 0, 40.0 at 0 etc
if let index = values.firstIndex(of: value) {
return "\(Int(value))° \(timesOfDay[index])"
}
let i = values.firstIndex(of: value)
//print(i as Any, "is index of where value shows up") //prints as nil is index of where value shows up for each i
return "\(Int(value))°"
}
}
Your assumption is not right. In IValueFormatter
, you can construct any string for the corresponding value. The reason you are seeing only value or timeOfDay is because you are constructing String
with """
notation that adds multiple lines whereas the label used for entry point may not calculate its size correctly because of this. You should create the String
as below and see what you have got
class ChartsFormatterToDegrees: IValueFormatter {
var timesOfDay = [String]()
var values = [Double]()
init(values: [Double], time: [String]) {
timesOfDay = time
values = values
}
func stringForValue(_ value: Double, entry: ChartDataEntry, dataSetIndex: Int, viewPortHandler: ViewPortHandler?) -> String {
if let index = values.firstIndex(of: value) {
return "\(Int(value))° \(timesOfDay[index])"
}
return "\(Int(value))°"
}
}