Charts
Charts copied to clipboard
[CombinedChart] Line entries disappears when changing `xAxis.spaceMax` or `xAxis.axisMaximum` with positive values (to fix the cropped bars)
- [x] I've read, understood, and done my best to follow the *CONTRIBUTING guidelines.
What did you do?
While using the CombinedChartView
with barData
and lineData
properly set, I realised the first and last bars cropped and after a few searches it's now clear that it's a known issue and when trying the suggested workarounds, I noticed that the line rendering breaks when changing the xAxis.spaceMax
or xAxis.axisMaximum
which internally ends up in the same code path.
The video below explains the case:
https://user-images.githubusercontent.com/469209/131644473-1da2f0ec-36da-4a50-a08d-41a81b22ae47.mov
What did you expect to happen?
I expected to fix the fist and last cropped bars issue without breaking the line chart.
What happened instead?
The line rendering breaks and disappears, ending up with just the very first entry.
Charts Environment
master but also tried the branch bugfix/xbounds-iterator
with no luck
Xcode version: 12.5
Swift version: 5.5
Platform(s) running Charts: iOS
macOS version running Xcode: Big Sur 11.5.2
Demo Project
Any help on how to workaround it is very appreciated!
Thank you
@liuxuan30 would you have any advice on that? (sorry to mark you)
@dsaliberti
I recently ran into similar issue. In the course of investigation the problem I also discovered that setting the axisMaximum
/axisMinimum
results in the same behaviour.
Debugging led me to a possibly faulty behaviour in the BarLineScatterCandleBubbleRenderer.XBounds
set(chart: BarLineScatterCandleBubbleChartDataProvider, dataSet: BarLineScatterCandleBubbleChartDataSetProtocol, animator: Animator?)
method:
open func set(chart: BarLineScatterCandleBubbleChartDataProvider,
dataSet: BarLineScatterCandleBubbleChartDataSetProtocol,
animator: Animator?)
{
let phaseX = Swift.max(0.0, Swift.min(1.0, animator?.phaseX ?? 1.0))
// This seems to be broken !
// When setting `axisMaximum`/`axisMinimum` and/or `spaceMax`/`spaceMin` these values result in `entryFrom`/`entryTo` being nil
let low = chart.lowestVisibleX
let high = chart.highestVisibleX
let entryFrom = dataSet.entryForXValue(low, closestToY: .nan, rounding: .down)
let entryTo = dataSet.entryForXValue(high, closestToY: .nan, rounding: .up)
self.min = entryFrom == nil ? 0 : dataSet.entryIndex(entry: entryFrom!)
self.max = entryTo == nil ? 0 : dataSet.entryIndex(entry: entryTo!)
range = Int(Double(self.max - self.min) * phaseX)
}
Simply changing the low
/high
calculation to:
let low: Double = Swift.max(chart.lowestVisibleX, dataSet.xMin)
let high: Double = Swift.min(chart.highestVisibleX, dataSet.xMax)
fixes the issue (at least for me). BUT I did not investigate the fix properly so I can't really tell if it breaks the code somewhere else.
@Arestronaut Thank you very much! This worked perfectly! Couldn't see any other problem until now. I'm using bar+line and no problems so far. 👍
@dsaliberti Great to hear that :) I’ll open up a PR for the fix soon
@dsaliberti Great to hear that :) I’ll open up a PR for the fix soon
yes please!
This fix works for me as well let low: Double = Swift.max(chart.lowestVisibleX, dataSet.xMin) let high: Double = Swift.min(chart.highestVisibleX, dataSet.xMax)
This is no quite a fix. The problem with xAxis.axisMaximum. If set axisMaximum MORE than real maximum of dataSet - data disappears. If set axisMaximum to real max of dataSet - all is good. p.s.: axisMinimum has no problems
I have trouble when draw group of datasources )
class XBounds`
func set(chart: BarLineScatterCandleBubbleChartDataProvider,
dataSet: BarLineScatterCandleBubbleChartDataSetProtocol,
animator: Animator?)
entryFrom = dataSet.entryForXValue(low, closestToY: .nan, rounding: .down) // - work perfect (set low in dataSet)
entryTo = dataSet.entryForXValue(high, closestToY: .nan, rounding: .up) // - can't find upper in passed data set
steps:
let high = chart.highestVisibleX // (set last item in chart by X)
dataSet.entryForXValue(high, closestToY: .nan, rounding: .up) // (take last item in chart by X, and .nan value as closesToY)
let index = entryIndex(x: xValue, closestToY: yValue, rounding: rounding) // (pass high as xValue, pass .nan as yValue)
// closest set to 0 by partitioningIndex { $0.x >= xValue }. As closest less than endIndex we move next step
closest > startIndex // false - and we move next step (without calculation)
switch rounding // .up - and we select branch for down where closest < startIndex... and next finish line :)
if !yValue.isNaN // false - and we move out, without searching :)
but upper value in this range the last item in dataSource, because I wait draw path, that described in dataSource ))