Scientific App: Double-tap reset zoom performance


#1

Hi all,

We have been using ShinobiCharts on an iOS scientific app for years. There are two series and we are using vertical line annotations to mark particular events along the series. Both gestureDoubleTapEnabledand gestureDoubleTapResetsZoomare set to YES, as is loadDataInBackground, and we are using arrays to cache the data points and dataPointsForSeriesAtIndex since there are routinely thousands of data points.

Everything is working pretty well. Zooming and panning are smooth. The issue is when you zoom in all the way (down to seconds on the time axis (x)) then double tap to reset zoom, CPU usage goes to 100% and the app frequently locks, even overheating devices in some cases.

This only occurs on one of the two places we are using the graph, which is one that is receiving new readings. In a log review, the double tap to reset zoom works smoothly, but it’s also a smaller view. I tried delaying accepting new readings for up to several seconds following a zoom reset, and that didn’t help. Instruments suggests ShinobiCharts spending a lot of time laying out subviews.

Any ideas of optimizations we can try? It’s blocking release of an updated version of the app, as it is.

I should note we are using storyboards and size classes, in case that is relevant.

Thanks!


#2

Hi crayfellow,

I’ve done some profiling to see what the bottlenecks are when double tap zooming. It appears that the calculation of tick marks is what takes up the largest percentage of time.

There are a couple of possibilities that might help performance:

  1. Turn off tick marks when double tap zooming
  2. Implement sChart:longestLabelStringOnAxis:

The first one might quite difficult at the moment as there is an issue with the sChartDidFinishZooming: delegate method - this would have been an ideal place to turn the charts tick labels back on. As a work around you could:

  • Use a double tap gesture recogniser to detect when the zoom starts.
  • Use the sChartIsZooming: method to detect when zooming is happening.
  • Use sChartIsZooming: method and the knowlege of the default axis range to detect when zooming has ended (once the axis has reached the default range you know zooming is done).

You will also have to handle the cases where the user interupts the zooming animation. To do this you might be able to detect single taps, pans and swipes and turn your tick labels back on.

I hope that gives you some ideas to try out! :slight_smile:


#3

Thanks @rgrey! Ticks were indeed the issue. I simply adjusted their frequency during zooming generally, and re-set them to the specified resolution when zooming and panning were finished. It has worked great.

The problem now, which the tick optimization has made unavoidably obvious, is another performance challenge: vertical line annotations. Since we can have up to 3600 data points per series, and an “alarm” or “warning” on each data point, we can likewise have thousands of vertical lines. Looking at it on the UI hierarchy view in Xcode, each vertical line is essentially drawn to infinity and clipped, and when there are thousands it looks like they end up occupying hundreds of layers that need to be clipped and composited. This ends up overloading CPU for minutes of deadlock on some devices (and xx seconds even on iPad Air).

Do you have a suggestion for optimizing annotations? I am adding them in sChartRenderFinished, and only 20 at a time. You can see the first 1000 or so are added on each render step, then it gets progressively slower. 

If we had a clean way to simply draw the series yellow or red for periods of warning/error, then go back to the default style color, that would actually be better.

Thanks! 


#4

it appears line series must have a way to change their color for only a portion, as this is what happens when the value goes out of the range set for the SChartNumberAxis. Ours for example is blue by default, but the portion that goes out of range is red, then back to blue when back in range.

Is there a way to access that color change to set a portion of the visible range to a different color/style to show warning and alarm state?


#5

Hi,

Unfortunately we don’t support altering the line colour for a specific range, however it can be set to different colours when above/below a baseline value (i.e.if your warning value is below 5, then set 'series.baseline = @10’ and alter the series’ ‘lineColorBelowBaseline’. However, you mention both a danger and warning colour so this may not be suitable for your scenario.

There are a couple of other things you may be able to try:

  1. Create a subclass of SChartLineSeries and override styleForPoint: to set a different point colour depending on the region your value falls within, note that you’ll need to enable ‘showPoints’ on the line series’ style object. However, you may not want to use coloured points to signify those that fall within your boundaries.

  2. As I currently understand it, you’re creating a verical line annotation for every point within a region. Unfortunately, due to the sheer number of annotation views that need to be updated as the view is zoomed this causes performance issues as you’ve discovered. You may be able to reduce the number of annotations by iterating through your data set and grouping all consecutive data points falling within that range using a vertical band annotation. This should help reduce the number of views that need to be updated as the chart is zoomed/panned.

    // Datapoints between 0 and 10 all fall within ‘warning’ range
    [_chart addAnnotation:[SChartAnnotation verticalBandAtPosition:@0 andMaxX:@10 withXAxis:_chart.xAxis andYAxis:_chart.yAxis withColor:[UIColor yellowColor]]];

    // Datapoints between 50 and 52 fall within ‘danger’ range
    [_chart addAnnotation:[SChartAnnotation verticalBandAtPosition:@50 andMaxX:@52 withXAxis:_chart.xAxis andYAxis:_chart.yAxis withColor:[UIColor redColor]]];

I hope those suggestions help, however please get back in touch if you’re still experiencing any issues.

Kind regards,

Sam