Nov 20, 2014

Drawing a line chart for iPad

What seemed like an easy job at the beginning, turned out to be a bit more challenging later: drawing a dynamic interactive line chart for iPad, with basic features like pan and zoom, with focus+context visualization, with around 2000 data points.

1. Draw the chart with CAShape layer:

I created a UIBezierpath, made this Bezier path to be the 'path' attribute of the shape layer, so this path is now the 'shape' for the shape layer.

Now for zooming (using pinch gesture)  I was rendering the path every time, that is regenerate all the points in the path, and drawing the whole path over again, which is obviously a slow process. I could just change the scaling factor of the layer, but then the line becomes blurry and thick. But calculating the 2000 data points takes a fare amount of time, and there should be a better way to do this.

2. Scroll with scrollview:

Then I found this thing called scrollview, which does an excellent job with scaling, but for zooming I needed better. And the scrolling itself seemed slower with this large path. Scrollview takes care of scrolling and paging with swipe gesture.

3. Performance enhancement: use several segmented paths, instead of one large path:

It turned out that performance of scrolling is much better if I draw several smaller CAShapeLayer than one large one, the longer the path is, the slower it is to render, but if I have several small paths, the rendering is smooth. so I created a data structure that stored all the small path segments, and then I put all the paths inside one scrollview.
After doing this scroll became very fast. Now it is time to fight with zooming again.

4. Use of tiled layer for better zooming:

Then I came to know about this another thing called CATiledLayer:
after improving the performance with the use of scrollview and multiple segmented paths, it's time to make the line chart zoom with pinch gesture. The same problem persists, I can directly change the scaling factor, but that makes the line very blurry, and I can recalculate all the points in the path which make it super slow and almost non-responsive. Then I found about the tiles layer approach, which is basically making the underlying layer of the view an instance of CATilesLayer, so whatever we draw in that view, are actually drawn in multiple tiles. So instead of drawing the Bezier paths inside shape layers, now I am drawing the Bezier paths inside a tiled layer. This tiles layer is good for zooming, why? Because it takes care of the scaling factor, redraws the content with appropriate number of tiles as we zoom, keeping the drawable object sharp!  That's exactly what I wanted. But only if that was all!

5. zoom only horizontally:
6. double layering, keeping the line crisp: