Core Plot is an open source graphing framework for Cocoa developers that makes it really easy to add graphs to your applications for the Mac and iPhone. Over the past half year, i’ve had the chance to use Core Plot in several of the internal applications i have been working on and have been very impressed with it, so i wanted to talk a little bit about how to use the library, in general, and how to make generate charts based on data in a Data Abstract for OS X application, in particular.
The example shown here is from a small iPhone application i wrote to keep track of the status of our Continuous Integration servers. The application shows the success (or errors) of any builds done on the machine, as well as – and this is the part where Core Plot comes in – graphs the number of tests that run and fail, over time:

In the application in question, the chart is being displayed in a cell within a UITableView, so our example starts out by implementing a custom UITableViewCell class that will host the graph. But the same general principles apply to show a chart anywhere else (and, replacing UIView with NSView, for using charts in a desktop app).
Core Plot uses a Core Animation as underlying technology, and as such draws itself in a specialized view class CPLayerHostingView. So we start out by crating this view, and adding it as sub-view:
- (id)initWithStyle:(UITableViewCellStyle)style { self = [super initWithStyle:style reuseIdentifier:@"MyChartCell"]; if (self) { CGRect frame = self.contentView.bounds; CPLayerHostingView *chartView = [[CPLayerHostingView alloc] initWithFrame: frame]; [self addSubview:chartView]; //...
Once the CPLayerHostingView is created, we can add a graphs to it, and configure them. There are several graph types supported, but we want a regular line graph with X/Y coordinates, so we’ll choose a CPXYGraph class. We’ll add the graph to the view, and set a padding, to give us some room between the edges of the table cell:
// create an CPXYGraph and host it inside the view CPTheme *theme = [CPTheme themeNamed:kCPPlainWhiteTheme]; graph = (CPXYGraph *)[theme newGraph]; chartView.hostedLayer = graph; graph.paddingLeft = 10.0; graph.paddingTop = 10.0; graph.paddingRight = 10.0; graph.paddingBottom = 10.0;
The next step is to set up a plot space. A plot space defines the coordinate system for one or more charts in a graph, essentially mapping logical values to the area in the graph. For example, your chart might show values ranging from, say, 0 to 1000. The plot space defines the scale of those values in relation to the graph. It also defines the visual range of values that can be seen on screen.
To match our X/Y graph, we’ll create a CPXYPlotSpace, and set it to a range of 0-100 for both axis:
CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace; plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0) length:CPDecimalFromFloat(100)]; plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0) length:CPDecimalFromFloat(100)];
Finally, we’ll set up to axis, for the X and Y coordinates. Axis provide labels and tick-marks for the viewer, to give context to the values being shown.
The X/Y graph already provides a set of axis in for of a – you guessed it – CPXYAxisSet class. There are several properties on the axis’ that are worth looking at at tweaking to to suite out needs:
- majorIntervalLength defines the number of units between “big” ticks on the axis. In this case it’s set to show one very 10 units. (if it were not for the exclusionRanges, discussed below, a numeric label would show for each major tick, as well).
- minorTicksPerInterval specified how many small ticks will be shown between each major one. in this case, a value of 2 indicated that small would show for 5, 15, 25, etc. (a value of 9 would show ticks for every single unit).
- Finally, exclusionRanges allows to define areas where no axis labels will be drawn. In my app, i don’t want to see any labels, so i set a range to exclude the entire visible graph
CPXYAxisSet *axisSet = (CPXYAxisSet *)graph.axisSet; CPXYAxis *x = axisSet.xAxis; x.majorIntervalLength = length:CPDecimalFromFloat(10); x.constantCoordinateValue = length:CPDecimalFromFloat(2); x.minorTicksPerInterval = 2; x.borderWidth = 0; x.labelExclusionRanges = [NSArray arrayWithObjects: [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(-100) length:CPDecimalFromFloat(300)], nil];; CPXYAxis *y = axisSet.yAxis; y.majorIntervalLength = length:CPDecimalFromFloat(10); y.minorTicksPerInterval = 1; y.constantCoordinateValue = length:CPDecimalFromFloat(2); y.labelExclusionRanges = [NSArray arrayWithObjects: [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(-100) length:CPDecimalFromFloat(300)], nil];
Our graph is now fully set up – except for one important part: we still need to define one or more charts to display actual values. My application needs to show two graphs on top of each other – one showing the total number of tests that ran, and another showing the number of failures.
Core Plot supports a variety of chart (or, plot) types, such as bar and pie chart, but for this scenario, we want a simple line graph, which is done via a CPScatterPlot. (A scatter plot actually allows to draw a plot with points scattered all over the place, not just as string y = f(x) values, but we will use it in the more simplistic case).
We’ll create a new CPScatterPlot instance, configure some properties such as its lineWidth and lineColor, and most importantly, set the dataSource to the object that will provide the plot with its data – in this case, self. To round things off, we also tell the plot to fill the area underneath it with a gradient, going from almost-transparent green to nothing. This gives the plot some depth, without hiding the other plots.
CPScatterPlot *dataSourceLinePlot = [[[CPScatterPlot alloc] init] autorelease]; dataSourceLinePlot.identifier = @"AllTests"; dataSourceLinePlot.dataLineStyle.lineWidth = 3.f; dataSourceLinePlot.dataLineStyle.lineColor = [CPColor greenColor]; dataSourceLinePlot.dataSource = self; [graph addPlot:dataSourceLinePlot]; // Put an area gradient under the plot above CPColor *areaColor = [CPColor colorWithComponentRed:0.3 green:1.0 blue:0.3 alpha:0.3]; CPGradient *areaGradient = [CPGradient gradientWithBeginningColor:areaColor endingColor:[CPColor clearColor]]; areaGradient.angle = -90.0f; CPFill *areaGradientFill = [CPFill fillWithGradient:areaGradient]; dataSourceLinePlot.areaFill = areaGradientFill; dataSourceLinePlot.areaBaseValue = CPDecimalFromString(@"1.75");
We repeat the above to add a second plot, except this time we use red for the line color and gradient, and set the identifier to @"FailedTests".
This was quite a bit setup, but our graph is now ready plot, and will ask its data source (our cell) for data by sending it messages from the CPPlotDataSource protocol. This protoco will be familiar to any Cocoa developer who worked with, for example, a UITableView or NSTableView, or any other control using a data source.
To the protocol, two methods need to be implemented. The first is numberOfRecordsForPlot: which will, simply enough, return the number of items (in case of a scatter plot, points, the chart will contain. The second can be one of three methods that return the actual data. We’ll implement numberForPlot:field:recordIndex:.
The data to display is retrieved via Data Abstract, using a simple DA SQL request on a table that contains all test runs:
NSString *sql = [NSString stringWithFormat:@"SELECT TOP 50 ID, _TotalTestCount, \ _FailedTestCount, FROM TestRuns ORDER BY ID DESC"]; testRuns = [[rda getDataTableWithSQL:sql] retain];
The _TotalTestCount and _FailedTestCount fields are server-calculated based on a relation table and contain the total number of run and failed tests for each test run. We can Key Path operators to get the maximum value, and adjust our plot space accordingly, so that the entire graph fits into the chart:
int count = [[testRuns rows] count]; int maxTests = [[[testRuns rows] valueForKeyPath:@"@max._TotalTestCount"] intValue]; CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace; plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0) length:CPDecimalFromFloat(count)]; plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0) length:CPDecimalFromFloat(maxTests)];
This is of course a bit redundant with the values we set earlier, but in real life application, the data access will usually happen in a different place than the initial setup (and data might get changed or refreshed during the course of the application), so i find it important to set up the graph properly to begin with, but then adjust the range to the real data.
With the data retrieved and the plot space adjusted, we can now implement the two data source methods.
numberOfRecordsForPlot: will simply return the number of rows in our table. numberForPlot:field:recordIndex will be called twice per data point, once for the X value (since we’re drawing a “non-scattered” plot, we’ll simply return the indexes) and one for the Y value, which we’ll retrieve from our data table. The different identifiers we assigned to the two plots will serve to distinguish which field to use for the Y value.
-(NSUInteger)numberOfRecordsForPlot:(CPPlot *)plot { return [rows count]; } -(NSNumber *)numberForPlot:(CPPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index { switch (fieldEnum) { case CPScatterPlotFieldX: { // inverse numbers, so first (latest) test run is on the right. int x = [rows count]-recordIndex; return [NSDecimalNumber numberWithInt:x]; } case CPScatterPlotFieldY: { if ([plot.identifier isEqual:@"AllTests"]) { float v = [[[rows objectAtIndex:index] valueForKey:@"_TotalTestCount"] floatValue]; return [NSNumber numberWithFloat:v]; } else { float v = [[[rows objectAtIndex:index] valueForKey:@"_FailedTestCount"] floatValue]; return [NSNumber numberWithFloat:v]; } } } return nil; }
And that’s it! You can find more info on Core Plot on their Google code page; i also recommend joining their mailing list for latest news and support.

[...] This post was mentioned on Twitter by marc hoffman, Drew McCormack and Barry Wark, Mark McLain. Mark McLain said: RT @bradlarson This is a nice article on getting started with Core Plot on the iPhone, written by @dwarfland: http://tinyurl.com/yah9kog [...]
Great article! However, have you tried using multiple plotspaces? It seems that all the code examples I’ve seen only use the default plotspace. I’d love to see some example code that uses 2 plotspaces to plot things to different scales, for instance.
i have, yes. it’s pretty straight forward, i’ll try to put something together for you later today.
Sorry for the delay. Adding extra plot spaces is pretty straight-forward. First, you create and add them to the graph, for example like this
And set them up just as you did the original one (setting range, etc) Secondly, you might wanna create an extra axis (but you don’t have to), and assign to it the right plotspace
Finally, when you add your plot, use the overloaded
addPlot:toPlotSpace:method. Important : DO NOT just assign the plotSpace property of the plot – that doesn;t seem to work. yo gotta ADD it to the right plotspace.hope that helps!
Can you send me an e-mail to follow up, I need to know if CorePlot can generate a graph like one that Excel has exported for me. I would have to show you the picture
that might be best done on the core-plot mailing list?
[...] Visualizing Data with Core-Plot | RemObjects Blogs (tags: iphone coreplot graphing coregraphics objective-c programming) [...]
Social comments and analytics for this post…
This post was mentioned on Twitter by dwarfland: @bynkii, @bradlarson: Wrote a little blog post about getting started with Core Plot you might find interesting: http://tinyurl.com/yah9kog...
Can you include a zipped up project file so we can try to run this sample?
Hi Marc, could you also send me an example xcode-proj. with the application mentioned above?
Kevin, Patrick: the actual code snippets here are part of a larger project that unfortunately i cannot jst share as is (and if in could, you would not have the back-end data). i’ll see what i can do to wrap this into a smaller project i can share, over the next week or so (but pls be patient with me, coz we got a busy week ahead, here at RemObjects)
Marc, Super Helpful! Your example pushed me in the right direction and over a hurdle I was having. Thanks for taking the time.
glad i could help!
Hi marc, good one.
I need your help in adding one more point to the existing plot to extend the chart dynamically. how to achieve it with core plot. I need to draw real time plot.
please help me..
[...] http://www.switchonthecode.com/tutorials/using-core-plot-in-an-iphone-application http://blogs.remobjects.com/blogs/mh/2010/01/26/p973 Categories: Cocoa, iPhone OS Tags: Comments (0) Trackbacks (0) Leave a comment [...]
I did all the setups needed for Settings. But I get error for CPColor.h and CPLayer.h. All other header files are OK. Please advise. I am facing tight deadline.
Hi Marc,
Thanks for the tutorial. I tried downloading the Framework from the google site . But I get connection refused. Did you face any of this problem?
Hi Marc,
I am new to iPhone SDK and need to use core plot to display ECG data streaming via the dock connection for a school project. I have that dock connection sorted out and now testing the serial commuication, small problem but would be able to resolve. I am not sure how to get started to install core plot. I would really apprecaite that you can help to clarify a few question that I have:
1) Must I install Mercurial?
a) If YES then is this the correct site “http://mercurial.selenic.com/wiki/”?
b) If YES, is this the correct version “2010-06-01 Mercurial 1.5.4 and TortoiseHg 1.0.4 released!”?
2) How do I use “hg clone http://core-plot.googlecode.com/hg/ core-plot” to get the source?
Your kins assistance is much apprecaited. Thank you.
Irwan
Irwan,
afaik core-plot is current;y only available via Mercurial checkout, yes. there’s no zip of the source or binary download i am aware of. any current version of Mercurial should work. Once Mercurial is installed, all you really need to do is run the “hg” command in Terminal.
hth.
Hi Marc,
Thanks for the reply. I finally manage to download core-plot and and run the tutorial on “http://www.switchonthecode.com/tutorials/using-core-plot-in-an-iphone-application” after much frustration to get it working with some modification due to the new Mac OS.
Is it possible to do up a small example xcode-proj using core-plot with the following features:
1) Input X Y value
2) Press button to update graph
3) Press another button to clear graph
Or perhaps some xcode-proj that you have already that does similar application to help me with my school project.
Your kind help is greatly appreciated. Thank you.
Irwan
i would recommend asking this on the official core-plot mailing list.
Thanks Marc for the advice. Can you please direct me which web site to go to? Prior to this, I did asked from “http://code.google.com/p/core-plot/” however till now, there is no respond.
apprecaite you kind help. Thanks.
Irwan
that is the right URL for the project. e discussion group is also at http://groups.google.com/group/coreplot-discuss
Hi Marc,
I am new to core-plot and tried out ur tutorial after setting up core plot in my iphone application. I wrote the code to include one line graph i.e until [graph addPlot:dataSourceLinePlot]; but no graph is drawn. I know the line graph cannot be drawn but i atleast expected the x and y coordinates. Am I missing anything?
it’s hard to tell, without more details. i suggest you post the full code you use to set up your graph to the core-plot mailing list.