ios-charts

iOS charts will not draw multiple datasets when the sets use different ranges of X-coordinates


When creating a line chart from more than one data sets, the line chart only shows one of the data sets and when zooming or panning the chart it crashes with Fatal error: Can't form Range with upperBound < lowerBound.

If I create the line chart from one data set it works as expected.

This problem only occurs when the two datasets have completely different ranges of X values.

The code below should draw a chart with x ranging from 0 to 19 (i.e. 2 datasets). But it only draws the second dataset. The chart crashes if you pan or zoom it.

If I edit the code, replacing for x in (10..<20) with for x in (0..<10), both datasets are correctly drawn and the chart does not crash.

To summarise: when adding two dataSets that have entries with different ranges of X coordinates the chart draws incorrectly and will crash.

Is there an iOS_charts API call needed to prevent this? How can I draw two datasets that do not have overlapping X-coordinates?

I've been able to produce the same crash when running code using this demo code if I modify it to create multiple datasets that have non-overlapping x-coordinates.

class ElevationChartViewController: UIViewController {
   
    @IBOutlet var chartView: LineChartView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        chartView.backgroundColor = .white
        chartView.legend.enabled = false
        chartView.maxVisibleCount = 20000
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        let dataSets = createChartDataSets()
        chartView.data = LineChartData(dataSets: dataSets)
    }
}

func createChartDataSets() -> [LineChartDataSet] {
      var dataSets = [LineChartDataSet]()
      var entriesOne = [ChartDataEntry]()
      var entriesTwo = [ChartDataEntry]()
      var y = 0.0
      for x in (0..<10) {
          entriesOne.append( ChartDataEntry(x: Double(x), y: y))
          y = y + 10
          if y > 60 {y = 0.0}
      }
      dataSets.append(LineChartDataSet(entriesOne))
      
      for x in (10..<20) {
          entriesTwo.append( ChartDataEntry(x: Double(x), y: y))
          y = y + 10
          if y > 50 {y = 0.0}
      }
      dataSets.append(LineChartDataSet(entriesTwo))

      return dataSets
}

Swift version: 5.4 Xcode 12.4 Observed running on a real iPhone 12 sw version 14.4 Charts v4.0.1


Solution

  • I have been facing a similar issue and this solution worked for me so far. Not sure about potential side effects that could arise from this. I have not tested with panning or zooming.

    Subclass LineChartDataSet and override entryIndex(x xValue:closestToY yValue:rounding) copy and paste the super implementation, but remove the guard statement at the top of the function

        var closest = partitioningIndex { $0.x >= xValue }
        guard closest < endIndex else { return -1 }
    

    and replace with

        var closest = partitioningIndex { $0.x >= xValue }
        if closest >= endIndex {
          closest = endIndex - 1
        }