4

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

pbm
  • 5,081
  • 4
  • 18
  • 31

1 Answers1

5

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
    }
Marrrrrrk
  • 226
  • 1
  • 2
  • 6
  • This works for me. And it works for panning and zooming. I'll accept it as an answer even though I acknowledge it could be poor solution in the long run and is a hack of the library. I'll report this "workaround" on the iOS Charts GitHub page. I'm also unsure about side effects. Thanks very, very much Marrrrk! – pbm Mar 11 '21 at 06:54
  • when `endIndex` is 0, this solution ends up with a crash caused by `self[closest].x -> self[-1].x` – kcome Oct 10 '21 at 23:47
  • Solved my issue as well! Thanks a lot! ‍♂️ – Serzhas Oct 18 '21 at 11:39
  • Note that when copying the `entryIndex()` function implementation, it has to be copied from `ChartDataSet`, not 'LineChartDataSet` (where it is not implemented/overridden). – Son of a Beach Apr 10 '22 at 04:18
  • This resolved the problem for me. – Son of a Beach Apr 11 '22 at 11:05