Multi-value bar chart?


#1

I’d like to configure a bar chart (SChartColumnSeries) to have two values for each series, a high and a low. I tried returning a SChartMultiYDataPoint in sChart:dataPointAtIndex:forSeriesAtIndex: but the lower value was just ignored. So I’m thinking I could use a SChartCandlestickSeries and only return a high and a low for the SChartMultiYDataPoint. This works pretty well except whereas SChartColumnSeries dynamically controls the width of the bar, SChartCandlestickSeries doesn’t so I have to set the stickWidth manually in order to make it look right (“right” meaning more like a bar chart than a candlestick chart).

My questions:

  • Is there an easier way to do what I’m doing?
  • Is there some way I can make the stickWidth adjust automatically like SChartColumnSeries?
  • If not, what’s the right place for me to change it while (for e.g.) the user is zooming in and out and how can this be calculated? Seems like I’d need the # of visible series in the chart frame but I don’t see a way to get this.

Thanks!


#2

Hi Jscalo,

SChartBarSeries and SChartColumn series were designed to always span from a baseline. I would have arrived at the same solution as you, that is, use a candlestick series. However, candlestick series don’t have the same scaling behaviour as you’ve noted.

The bar column width is actually calculated once per data load. You’ll notice if you zoom into a bar series, the bars actually get wider / thinner. You should be able to simply add your candlestick width algorithm to the sChartDidFinishLoadingData: method. You can then update your candlestick series width based off your algorithm.

// Assuming your candlestick series is the first series.
SChartCandlestickSeries *candlestickSeries = (SChartLineSeries *)chart.series[0];
candlestickSeries.style.trunkWidth = [self calculateTrunkForNumberOfSeries:... xAxisRange:...];

With regards to the actual ‘width algorithm’ itself, you’ll probably want to look at the chart’s x-range and the number of series. Both of which should be easily inspectable via the chart.

NSUInteger numberOfSeries = chart.series.count;

Thanks,
Jan Akerman


#3

Thanks, Jan. So I’m on the right track, that’s good :wink:

To make this behave like a SChartBarSeries I need the widths to update as the user zooms in and out, but sChartDidFinishLoadingData: only fires when the chart actually loads data. Is there a better hook to update while zooming?

Also I’m not clear on how to determine the # of “columns” that are visible in the chart frame. In my case, chart.series.count will always be 1. I can get the data range with chart.xAxis.visibleRange but I’d have to dig into the data source to calculate number of visible data points for a given range and I doubt that would be performant while the user is zooming. Is there a call that can tell me the # of visible series?

Update: sChartRenderFinished: seems a good place to adjust the widths, but I’m still unclear on how to calculate the # of bars in the chart frame. Thanks.


#4

Well, I think I stumbled upon a potentially awesome solution. If I subclass SChartCandlestickSeries and do:

- (SChartCandlestickSeriesStyle *)styleForPoint:(id<SChartData>)point previousPoint:(id<SChartData>)prevPoint

{
    UIColor *barColor = [UIColor colorWithWhite:0.75 alpha:.9];
    SChartCandlestickSeriesStyle *style = [super styleForPoint:point previousPoint:prevPoint];
    // Leaving any of these nil will throw an exception.
    style.stickColor = barColor;
    style.outlineColor = barColor;
    style.risingColorGradient = barColor;
    style.fallingColorGradient = barColor;
    style.fallingColor = barColor;
    style.risingColor = barColor;
    style.outlineWidth = @(0);
    style.trunkWidth = @(0);
    style.armWidth = @(0);
    
    return style;
}

Then the bar (well, stick in this case) widths are calculated for me a la column series. Can I bank on this behavior? If so, I think it’s perfect.


#5

Hi Jscalo,

With regards to getting the number of columns currently visible in the current range, you would need to do that yourself. It should be quite easy, you’ve got access to your datasource, and you’ve got access to your X-axis range. I understand the concern for performance, but I’m not so sure it would be a problem - it would definitely be worth chucking something quick together just as a prototype - it’s always best not to prematurely optimise!  :laughing:

I think I’m going to need a bit of clarification as to what your original issue was with the bar / candlestick behaviour. After having a re-read, I’m not so sure I understand what the problem is. I’ve uploaded two screen captures of me zooming with the bar chart and candlestick chart respectively. The zooming behaviour with regards to the width looks the same to me.

I must either have my chart set up differently, or be missing something obvious. I should be able to answer the last question about your magic solution when I understand what’s going on!  :rage:

Thanks,
Jan


#6

OK, you’re right- I was confused about the terminology: arm vs. stick vs. trunk. (Too bad you can’t display an image with HeaderDoc. A diagram of the chart components and their labels would be super helpful.) And so I was coloring the stick, not the trunk. No subclassing should be necessary here.

Last issue: there’s a 1pt “notch” in the middle of the trunk on either side - you can see it in your uploaded movie - and it throws off the bar chart illusion. Aside from not using an outline, is there any way to get rid of it?


#7

It’s actually a visual artefact we have fixed locally. It’ll be included in the next release but I can’t say exactly when that’ll be at this point in time. If you send an email into info@shinobicontrols.com I can pop you on our ‘Awaiting Component Release’ list and we’ll give you a mail when it’s ready?

Jan


#8

Understood. I’ll keep any eye out for it in the release notes.