Creating a Graph With Quartz 2D: Part 3

This entry is part 3 of 5 in the series Graphing with Quartz 2D

Graphing with Quartz 2D

To create a line graph, I am going to reuse the same project that we used for drawing bars in the previous part of the series. We won’t need the logic for drawing bars anymore, so comment out a dozen of lines of code in the end of the drawRect method, those that deal with bars.

I’ve also changed the value of kOffsetX to 0, so that the line graph starts from the left edge of the view. We’ll need to add a couple of values to the dataset, and you can choose any values between 0 and 1. Here is what I’ve chosen:

float data[] = {0.7, 0.4, 0.9, 1.0, 0.2, 0.85, 0.11, 0.75, 0.53, 0.44, 0.88, 0.77, 0.99, 0.55};

Create a new empty method, making sure that it goes before the drawRect:

- (void)drawLineGraphWithContext:(CGContextRef)ctx
{

}

We’ll be gradually filling this method with code. The first step is to set up the environment, in this case to define the width and the color of the line we are going to use. The following two lines of code shouldn’t surprise you:

CGContextSetLineWidth(ctx, 2.0); 
CGContextSetStrokeColorWithColor(ctx, [[UIColor colorWithRed:1.0 green:0.5 blue:0 alpha:1.0] CGColor]);

You are already familiar with drawing paths – we’ve drawn bars as paths – so the following lines of code, although slightly different, should be fully comprehensible:

int maxGraphHeight = kGraphHeight - kOffsetY;
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, kOffsetX, kGraphHeight - maxGraphHeight * data[0]);

for (int i = 1; i < sizeof(data); i++) 
{
    CGContextAddLineToPoint(ctx, kOffsetX + i * kStepX, kGraphHeight - maxGraphHeight * data[i]);
}

CGContextDrawPath(ctx, kCGPathStroke);

This is all we need to draw a simple line graph. Run the app, and you should see the following:

Quartz 2D Part III Figure 1

Figure 1

For some simple cases this graph might be acceptable, but there are a number of ways in which we can enhance it. One of them is to make the data points more noticeable.

Emphasising Data Points

One popular solution is to draw a little circle on top of each datapoint, so let’s do that. First of all, we’ll want the circles to be filled with color, so add the following line of code to the very end of the drawLineGraphWithContext method:

CGContextSetFillColorWithColor(ctx, [[UIColor colorWithRed:1.0 green:0.5 blue:0 alpha:1.0] CGColor]);

Next, let’s define the circle radius in the header file:

#define kCircleRadius 3

In Quartz 2D, to draw a circle or ellipse, we first need to create a rectangle (CGRect) that will define that circle’s dimensions. The simplest way to create such a rectangle is to use the CGRectMake function that takes four parameters: the x and y coordinates of the upper left corner of the rectangle, then its width and height.

We’ll be creating the rectangles in such a way that their centers coincide with the points through which we’ve just drawn the line graph. Then we’ll inscribe a circle into each of the rectangles, and finally, we’ll stroke and fill the circles. All in all, the code looks like this:

for (int i = 1; i < sizeof(data) - 1; i++) 
{
    float x = kOffsetX + i * kStepX;
    float y = kGraphHeight - maxGraphHeight * data[i];
    CGRect rect = CGRectMake(x - kCircleRadius, y - kCircleRadius, 2 * kCircleRadius, 2 * kCircleRadius);
    CGContextAddEllipseInRect(ctx, rect);
}
CGContextDrawPath(ctx, kCGPathFillStroke);

Add it to the end of the drawLineGraphWithContext method, and this is the result that you should see:

Quartz 2D Part III Figure 2

Figure 2

The next enhancement we might want to make is filling the space underneath the graph with a color.

Filling the Graph

The steps needed to create a graph filled with color are very similar to those for creating the graph line itself, the only difference is that we need to create a closed path. Let’s see how to do that.

At the beginning of drawLineGraphWithContext method, right after defining the maxGraphHeight, let’s specify a fill color – the same color we are using for drawing the graph itself, but semi-transparent:

CGContextSetFillColorWithColor(ctx, [[UIColor colorWithRed:1.0 green:0.5 blue:0 alpha:0.5] CGColor]);

Then we basically draw the graph, but add a few lines to surround a closed space. Finally, we fill the resulting path but we don’t stroke it. Follow the code and you’ll see what’s going on:

CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, kOffsetX, kGraphHeight);
CGContextAddLineToPoint(ctx, kOffsetX, kGraphHeight - maxGraphHeight * data[0]);
for (int i = 1; i < sizeof(data); i++) 
{
    CGContextAddLineToPoint(ctx, kOffsetX + i * kStepX, kGraphHeight - maxGraphHeight * data[i]);
}
CGContextAddLineToPoint(ctx, kOffsetX + (sizeof(data) - 1) * kStepX, kGraphHeight);
CGContextClosePath(ctx);

CGContextDrawPath(ctx, kCGPathFill);

After the fill is complete, we draw the graph line itself and the circles, as before, on top of it. If you run the app now, you’ll see that the graph became a bit nicer:

Quartz 2D Part III Figure 3

Figure 3

To make it nicer still, we might want to fill the graph with a gradient. You already know how to create a gradient, so it should be easier this second time around.

Filling the Graph with a Gradient

Here is the code that prepares the gradient, with no comments this time. Insert it right before the code that we’ve written in the previous section for creating a solid fill:

CGGradientRef gradient;
CGColorSpaceRef colorspace;
size_t num_locations = 2;
CGFloat locations[2] = {0.0, 1.0};
CGFloat components[8] = {1.0, 0.5, 0.0, 0.2,  // Start color
        1.0, 0.5, 0.0, 1.0}; // End color
colorspace = CGColorSpaceCreateDeviceRGB(); 
gradient = CGGradientCreateWithColorComponents(colorspace, components, locations, num_locations);

CGPoint startPoint, endPoint;
startPoint.x = kOffsetX;
startPoint.y = kGraphHeight;
endPoint.x = kOffsetX;
endPoint.y = kOffsetY;

Now, we are going to reuse the closed path we’ve created in the previous section. This time it will work as a clipping path. Comment out this line:

CGContextDrawPath(ctx, kCGPathFill);

and replace it with the other two:

CGContextSaveGState(ctx);
CGContextClip(ctx);

We are ready to pour the gradient into the graph now. Go ahead:

CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0);

Finally, do the cleanup:

CGContextRestoreGState(ctx);
CGColorSpaceRelease(colorspace);
CGGradientRelease(gradient);

Here is the result that you should see:

Quartz 2D Part III Figure 4

Figure 4

Not bad, is it? And of course, you can tweak the gradient if you wish.

Now we know how to draw both a bar graph and a line graph. They look good but they are not interactive yet. It would be nice if, say, when the user taps a bar, the graph would display a message appropriate for that bar. This is exactly what we are going to deal with in the next part of the series.

Quartz 2D Index

Alexander Kolesnikov’s series on Creating a Graph using Quartz 2D was split into 5 parts. You can refer to the series using the Quartz 2D Tag and access the individual articles using the links below.

Graphing with Quartz 2D

<< Creating a Graph With Quartz 2D: Part 2Creating a Graph With Quartz 2D: Part 4 >>

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • shabnam

    these are great info ! thank you so much for all of these tutorials !

    Could you tell me how I can draw x and y axis for my graph with numbers on them ?
    (or at least giving me a hint ! :-))

    Thanks

  • http://3easy.org Paul Bridgestock

    We can absolutely oblige that request. Alexander is working on the final piece of the series right now, which includes the addition of labelling. Make sure you have read all four parts published so far, and stay tuned for the fifth and final part very soon.

  • http://crestwhitestripscoupon908.blog.com/ Vietascabbesy

    It is the happiest time of my life so far, when I am watching these} funny videos at this place, because after complete day working I was so tired and now feeling sound.

  • http://unitedhealthcare309.squarespace.com/ boopoxcom

    I like to work on Personal home pages rather than .NET, although .NET presents the feature of drag and drop elements, however I love Personal home pages a lot.

    http://www.thoughts.com/unitedhealthcareproviders8/ – United Healthcare Providers

  • Josh

    Couldn’t be ANYMORE thankful! THANKYOU THANKYOU THANKYOU!!!!!

  • http://www.bridgetech.comlu.com Mark

    Hi great guide but I’ve got a problem. I can’t get the transparency or gradient to work on the file for the line graph. I’m just getting a solid fill. What am I doing wrong?

    I’m a bit confused by the order of the various part of code in this section. Maybe if you could include the entire – (void)drawLineGraphWithContext:(CGContextRef)ctx method then it would be clearer.

  • Guilherme

    Man, this tutorial is just amazing.

    Many thanks for the incredible help

  • Marco Almeida

    The tutorial is indeed great, but I wasn’t able to make it work here the line draw example. I also downloaded the whole project from github and copied and pasted the code for line graph but still not showing any line. Only the background and grid is on the screen.

    I am using Xcode 4.5.2. Any help???

    Thanks!!!

  • Marco Almeida

    I am also not getting the gradient…

  • Robert Touchette

    Thanks for this awesome tutorial, but I am not able to make the line graph works. Apparently, drawLineGraphWithContext method is not being called. I am only able to draw the grid.

    Do you have any suggestion in this case?

  • Robert Touchette

    Your project on your github repository doesn’t work with line graph either.

  • Robert Touchette

    My bad,

    drawLineGraphWithContext should be called in drawRect method to make it works.

    – Great tutorial!