Blog

Back to Blog

Taking a ShinobiChart screenshot from your app

Posted on 27 Sep 2012 Written by Stuart Grey

Now updated for iOS7 and ShinobiCharts 2.5

It’s been a while since this blog post was originally published, and a lot has changed since then – please take a look at the more recent blog post.

Overview

You’ve created the perfect charts in your app – and now you want to let your users share them. Maybe post a picture to twitter or email it to a friend? There are lots of well documented ways to capture UIViews and their contents into an image. Indeed, there are also many of the same examples for the openGL content that is utilized in ShinobiCharts. The trick is to make them work together and integrate them into the ShinobiCharts API – and here we’ll do just that.

Capturing the openGL elements

A large part of the canvas on a ShinobiChart is drawn using openGL to achieve the high levels of performance you experience. The first part of building up the chart image is to capture this screen information from the render buffers. To do this we’re going to have to dig a bit deeper than usual into the ShinobiCharts API – but before we do we’ll create a category on the openGL drawing class used by ShinobiCharts.

@interface SChartGLView (Screenshot)
- (UIImage*)snapshot;
@end

@implementation SChartGLView (Screenshot)
   
- (UIImage*)snapshot
{

This gives us a way to extend the current functionality to include a snapshot method. To continue any further we’ll need to know a bit more about the openGL class created for ShinobiCharts – SChartGLView. This file (and others like it) can be found in the headers bundled with the ShinobiCharts framework.

FolderStructure

At the top of our category code we import this header and start to work out what we need. We’ll discuss the high level process here – the attached code has all of the details.

  1. We need to work out the size of our openGL area to allow us to create a copy of it. We do this by grabbing the information from the render buffer.

        // Get the size of the backing CAEAGLLayer     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight); 
  2. Once that’s done we can pull out the information from the frame buffer. But first we have to work out which frame buffer to use. glReadPixels – the method we’ll use to grab the data – doesn’t support anti-aliased data. Luckily, a frame buffer has already been created for us with all of the anti-aliased data resolved. _frameBufferMSAA is resolved into _frameBuffer using glResolveMultisampleFramebufferAPPLE();

  3. Now that we’ve bound the correct frame buffer and read the pixels we just need to flip the coordinates around as openGL and the iOS graphics context use different origins. The raw data from the buffer is copied to the graphics context and from there it’s relatively simple to grab a UIImage.

We bundle all of that code into SChartGLView+Screenshot.h to extend the openGL functions with our snapshot function.

Capture the UI elements and merge in the openGL image

Now that we have a method for grabbing the openGL components as a UIImage, we need the same for the remaining UI elements of the chart. Once we’ve checked the scale of the screen, this is a relatively simple process of grabbing the current graphics context

    //Grab the chart image (minus GL)
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, [UIScreen mainScreen].scale);
    } else {
        UIGraphicsBeginImageContext(self.frame.size);    
    }
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *chartImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

We now have two images of the chart – each with a different set of components:

ChartImages

We then merge the two images using a UIImageView and create our composite UIImage. Leaving us with a snapshot method on the chart to create the perfect screen grab for our users to share!

UIImage *screenshot = [chart snapshot];

Summary

We’ve created two categories on ShinobiChart components to extend the functionality for capturing in-app screenshots. The two categories are SChartGLView+Screenshot.h and ShinobiChart+Screenshot.h available here. We simply include these files in our code and the methods are available for use.

Back to Blog