Adding charts to your iPhone / iPad App using Core Plot 0.9
Posted by John Wordsworth on October 10, 2011 in iOS Development, OSX Development tagged with Charts, CorePlot, Graphs, iOS Charts, iPhone ChartIt’s unfortunate that Apple do not provide a charting library bundled with their frameworks, for I suspect that would save many developers the trouble of finding a charting library and deciphering the documentation. I’ve come to the conclusion that CorePlot is the best bet at the moment – it’s powerful and compatible with both iOS and OSX but unfortunately the documentation for isn’t great. So, we’re going to go through the process of adding a simple scatter chart to an iPad application.
Our goal is to build an application that contains a simple scatter graph with data stored in an NSArray (which could easily have been loaded from a CSV file). We aim to add this in a generic fashion, in a sub-view of a NIB file – so that you could drop this work into an app you’re working on without much work. We target the iPad, but the same method will work for the iPhone without any changes and will also work on OSX if you make a couple of minor changes.
This tutorial assumes that basic iOS programming knowledge and experience. Before we start, it’s worth noting that CorePlot is a very flexible library – it just takes a bit of getting used to. Unfortunately, the base documentation doesn’t help much when you’re getting started – so I’ll try to expose as much as I currently know about the library.
1. Starting a new Project
We’re going to develop a simple single-view iPad application – but you can add your CorePlot to any existing view in an application using the same method presented here. I use a class prefix of TUT to represent any classes I build during this tutorial.
2. Acquiring and Installing CorePlot
When your project is ready and waiting, the first thing we will do is install the CorePlot library, which will expose the classes necessary for you to get started in adding your scatterplot to your application. Head over to the CorePlot website and download the latest zip file. This tutorial is based on CorePlot 0.9, so if the code has changed significantly in the future – you might want to download version 0.9 to work through this tutorial and then figure out how the code has changed later.
When your download is complete, unzip the files in your Downloads directory. We’re going to install the static library, where the code has already been compiled as libCorePlot-CocoaTouch.a, but you could install the source-code version instead if you prefer – which would then recompile CorePlot when you build your app. Both methods are essentially the same, as you’ll still need to include the header files so that your code can access the methods. I keep my external libraries separate from my own source code, so we’re going to add a new folder at the root of our project called ‘Libs’ and then create a sub-directory inside that called ‘CorePlot’. Now, from the zip file that you downloaded – you want to copy libCorePlot-CocoaTouch.a and the folder CorePlotHeaders from the folder CorePlot_0.9/Binaries/iOS/. Your directory structure should look like this;
Now that your files are in place, you just need to add them to your Xcode project. Drag and drop the whole Libs folder onto your Xcode file tree. Or, you can use the File -> Add Files to “Core Plot Introduction” option from the files menu and select the Libs folder. There should now be a Libs folder in your project sidebar containing the CorePlotHeaders and the libCorePlot file. Now, there are a couple of settings we need to tweak to get CorePlot working before we can start to implement our scatter plot. Select your project from the navigator panel in Xcode and then highlight the Build Settings tab. Filter the settings or locate ‘Other Linker Flags’ from this list and then add -ObjC -all_load to these settings, as shown in the next figure;
There’s just one more step before we can start working with CorePlot in our application. As CorePlot makes use of Apple’s Core Animation code, which is a part of the QuartzCore framework – we need to ensure that our project is linking against the QuartzCore code. With the project still selected in Xcode, highlight the Build Phases tab. Expand the Link Binary with Libraries panel and click on the plus button at the bottom. Now select QuartzCore.framework from the list and hit ok. I usually copy the reference in my project into the Framework folder to be tidy – but this isn’t a necessity.
3. Adding a Graph Hosting View to our Interface
In order to add a CorePlot chart space to our application, we start by setting up a ‘Hosting View’ which we will later attach our graph(s) too. We’re just creating a placeholder in our NIB at the moment, into which we will tell CorePlot to start drawing using our code later on.
We start by adding a label to the top of the interface with some dummy text and change the background colour of the main XIB view to Scroll View Textured Background Color to make it looks swish. Next, drag and drop a UIView component from the object library onto your project. You would normally make this as big as possible, as we would adding padding within CorePlot (to leave spill over space for axis labels). However, for this tutorial – I’ve left a border to make it easier to see what’s going on in the figures. Next, select the view you’ve just added and change it’s class in the property panel to the right. It’s class should be; CPTGraphHostingView. You should now have something that looks like the next figure (notice the object hierarchy on the left and the UIView’s class at the top right).
4. Creating our Scatter Plot object
CorePlot acquires data for its plots much like UITableView does – by calling specific methods on the class you have set as the Graph’s delegate. Incidentally, it also delegates many of it’s features this way too, for instance if you want the user to be able to interact with the chart. So, much like a table view, you could set the main ViewController as your delegate and not add an extra class to your code. However, in order to keep your charts re-usable and interchangeable, I think it’s best to create a new class that will do all the work for your chart.
So, we add a new class to our project TUTSimpleScatterPlot, which is simply a subclass of NSObject. In TUTSimpleScatterPlot.h we need to import the CorePlot libraries; #import “CorePlot-CocoaTouch.h” and then we define a few properties for the objects we need to keep a reference too. The header file for our chart object looks like this…
TUTSimpleScatterPlot.h
#import
#import "CorePlot-CocoaTouch.h"
@interface TUTSimpleScatterPlot : NSObject {
CPTGraphHostingView *_hostingView;
CPTXYGraph *_graph;
NSMutableArray *_graphData;
}
@property (nonatomic, retain) CPTGraphHostingView *hostingView;
@property (nonatomic, retain) CPTXYGraph *graph;
@property (nonatomic, retain) NSMutableArray *graphData;
// Method to create this object and attach it to it's hosting view.
-(id)initWithHostingView:(CPTGraphHostingView *)hostingView andData:(NSMutableArray *)data;
// Specific code that creates the scatter plot.
-(void)initialisePlot;
@end
We need to keep hold of 3 objects to make our chart work. We will pass the hosting view from our XIB file and an array of data when we call initWithHostingView:andData: while creating an instance of our chart. We create the CPTXYGraph and setup our chart when initialisePlot is called – which we will usually call shortly after we initialise our object. Notice we have stated that our object responds to the CPTScatterPlotDataSource delegate methods in the class definition.
Next, we’re going to write the init and initialisePlot methods, as well as the methods from the CPTScatterPlotDataSource protocol required to get our chart working. In time honoured tradition, I’ll throw the code at you first, and then explain it. While it looks like a lot of code, most of it involves configuring certain graph elements – it’s simpler than it first appears.
TUTSimpleScatterPlot.m
#import "TUTSimpleScatterPlot.h"
@implementation TUTSimpleScatterPlot
@synthesize hostingView = _hostingView;
@synthesize graph = _graph;
@synthesize graphData = _graphData;
// Initialise the scatter plot in the provided hosting view with the provided data.
// The data array should contain NSValue objects each representing a CGPoint.
-(id)initWithHostingView:(CPTGraphHostingView *)hostingView andData:(NSMutableArray *)data
{
self = [super init];
if ( self != nil ) {
self.hostingView = hostingView;
self.graphData = data;
self.graph = nil;
}
return self;
}
// This does the actual work of creating the plot if we don't already have a graph object.
-(void)initialisePlot
{
// Start with some simple sanity checks before we kick off
if ( (self.hostingView == nil) || (self.graphData == nil) ) {
NSLog(@"TUTSimpleScatterPlot: Cannot initialise plot without hosting view or data.");
return;
}
if ( self.graph != nil ) {
NSLog(@"TUTSimpleScatterPlot: Graph object already exists.");
return;
}
// Create a graph object which we will use to host just one scatter plot.
CGRect frame = [self.hostingView bounds];
self.graph = [[[CPTXYGraph alloc] initWithFrame:frame] autorelease];
// Add some padding to the graph, with more at the bottom for axis labels.
self.graph.plotAreaFrame.paddingTop = 20.0f;
self.graph.plotAreaFrame.paddingRight = 20.0f;
self.graph.plotAreaFrame.paddingBottom = 50.0f;
self.graph.plotAreaFrame.paddingLeft = 20.0f;
// Tie the graph we've created with the hosting view.
self.hostingView.hostedGraph = self.graph;
// If you want to use one of the default themes - apply that here.
//[self.graph applyTheme:[CPTTheme themeNamed:kCPTDarkGradientTheme]];
// Create a line style that we will apply to the axis and data line.
CPTMutableLineStyle *lineStyle = [CPTMutableLineStyle lineStyle];
lineStyle.lineColor = [CPTColor whiteColor];
lineStyle.lineWidth = 2.0f;
// Create a text style that we will use for the axis labels.
CPTMutableTextStyle *textStyle = [CPTMutableTextStyle textStyle];
textStyle.fontName = @"Helvetica";
textStyle.fontSize = 14;
textStyle.color = [CPTColor whiteColor];
// Create the plot symbol we're going to use.
CPTPlotSymbol *plotSymbol = [CPTPlotSymbol crossPlotSymbol];
plotSymbol.lineStyle = lineStyle;
plotSymbol.size = CGSizeMake(8.0, 8.0);
// Setup some floats that represent the min/max values on our axis.
float xAxisMin = -10;
float xAxisMax = 10;
float yAxisMin = 0;
float yAxisMax = 100;
// We modify the graph's plot space to setup the axis' min / max values.
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)self.graph.defaultPlotSpace;
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(xAxisMin) length:CPTDecimalFromFloat(xAxisMax - xAxisMin)];
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(yAxisMin) length:CPTDecimalFromFloat(yAxisMax - yAxisMin)];
// Modify the graph's axis with a label, line style, etc.
CPTXYAxisSet *axisSet = (CPTXYAxisSet *)self.graph.axisSet;
axisSet.xAxis.title = @"Data X";
axisSet.xAxis.titleTextStyle = textStyle;
axisSet.xAxis.titleOffset = 30.0f;
axisSet.xAxis.axisLineStyle = lineStyle;
axisSet.xAxis.majorTickLineStyle = lineStyle;
axisSet.xAxis.minorTickLineStyle = lineStyle;
axisSet.xAxis.labelTextStyle = textStyle;
axisSet.xAxis.labelOffset = 3.0f;
axisSet.xAxis.majorIntervalLength = CPTDecimalFromFloat(2.0f);
axisSet.xAxis.minorTicksPerInterval = 1;
axisSet.xAxis.minorTickLength = 5.0f;
axisSet.xAxis.majorTickLength = 7.0f;
axisSet.yAxis.title = @"Data Y";
axisSet.yAxis.titleTextStyle = textStyle;
axisSet.yAxis.titleOffset = 40.0f;
axisSet.yAxis.axisLineStyle = lineStyle;
axisSet.yAxis.majorTickLineStyle = lineStyle;
axisSet.yAxis.minorTickLineStyle = lineStyle;
axisSet.yAxis.labelTextStyle = textStyle;
axisSet.yAxis.labelOffset = 3.0f;
axisSet.yAxis.majorIntervalLength = CPTDecimalFromFloat(10.0f);
axisSet.yAxis.minorTicksPerInterval = 1;
axisSet.yAxis.minorTickLength = 5.0f;
axisSet.yAxis.majorTickLength = 7.0f;
// Add a plot to our graph and axis. We give it an identifier so that we
// could add multiple plots (data lines) to the same graph if necessary.
CPTScatterPlot *plot = [[[CPTScatterPlot alloc] init] autorelease];
plot.dataSource = self;
plot.identifier = @"mainplot";
plot.dataLineStyle = lineStyle;
plot.plotSymbol = plotSymbol;
[self.graph addPlot:plot];
}
// Delegate method that returns the number of points on the plot
-(NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot
{
if ( [plot.identifier isEqual:@"mainplot"] )
{
return [self.graphData count];
}
return 0;
}
// Delegate method that returns a single X or Y value for a given plot.
-(NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
if ( [plot.identifier isEqual:@"mainplot"] )
{
NSValue *value = [self.graphData objectAtIndex:index];
CGPoint point = [value CGPointValue];
// FieldEnum determines if we return an X or Y value.
if ( fieldEnum == CPTScatterPlotFieldX )
{
return [NSNumber numberWithFloat:point.x];
}
else // Y-Axis
{
return [NSNumber numberWithFloat:point.y];
}
}
return [NSNumber numberWithFloat:0];
}
@end
First of all, lets look at each of the methods above and describe what they do, and then we’ll look at some of the Core Plot objects that we make use of in the initialisePlot method.
So, initWithHostingView:andData simple initialises this object and retains a reference to the hosting view and data array which is fed to this method. initialisePlot is designed to be called shortly after the init method, but has been separated so that you can choose when the brunt of the work is done in creating the graph. This method provides the bulk of this class, which mostly consists of setting up the graph object with the required axis settings, line styles and label styles etc.
numberOfReportsForPlot: is a delegate method which simply returns the number of unique data points that exist for a given plot. If you are using this object as a delegate for multiple plots, you can determine which plot you should return data for by interrogating the identifier property – which you set earlier in the initialisePlot method. Finally, numberForPlot:field:recordIndex: will be called twice per data point in each plot. fieldEnum tells you whether the graph object is expecting the X or Y value for a given data point at index. Like with numberOfReportsForPlot, you can differentiate between multiple plots by first checking the identifier of the plot.
The intialisePlot method is fairly self-explanatory when reading it with the comments. However, it took me a while to get my head around the different objects made available by CorePlot. The CPTXYGraph represents an area into which a 2-dimensional graph will be drawn inside of a HostingView which exists in the parent UIView. The graph comes with it’s own plotAreaFrame (the area inside the graph that the actual data will be rendered), a plotSpace (the minimum and maximum values of X and Y that will be drawn on your chart) and an axisSet (essentially style and rendering options for the axis on this graph). You generally pull out a reference from the graph object for each of these and then manipulate them to design your chart.
The most prominent style and design objects you will use are; CPTMutableLineStyle - which represents a set of options for how a given line should be drawn (width / colour), CPTMutableTextStyle - a set of objects describing how a text label should be drawn (font, size, colour) and CPTPlotSymbol - representing an ellipse or cross for instance that will appear at the actual data points on your chart lines. When all of your chart framework is setup, you can then add one or more CPTScatterPlot objects to your graph – each one representing a unique data set or line on your chart. We only add one in this example and set the delegate to self (as we’ve set the delegate methods which return the data), a line style and a plot symbol. We also set an identifier incase we add more plots in the future so that the delegates can tell which plot the graph is expecting data for when calling a delegate method.
5. Hooking it all together
Last but not least, we need to bring all of these elements together and look at our creation. So, we need to create an IBOutlet for our Graph Hosting View in TUTViewController. Your TUTViewController.h should look like the following;
TUTViewController.h
#import
#import "CorePlot-CocoaTouch.h"
#import "TUTSimpleScatterPlot.h"
@interface TUTViewController : UIViewController {
IBOutlet CPTGraphHostingView *_graphHostingView;
TUTSimpleScatterPlot *_scatterPlot;
}
@property (nonatomic, retain) TUTSimpleScatterPlot *scatterPlot;
@end
Now, don’t forget to hook your CPTGraphHostingView from the TUTViewController back to the view that you added to the NIB right back at the start of this tutorial. Load up TUTViewController.xib and Ctrl-Click-Drag from ‘Files Owner’ to the Hosting View and then select graphHostingView as the outlet. We’ll create some sample data and then create the scatterPlot object in the viewWillAppear method of the View Controller;
TUTViewController.m
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSMutableArray *data = [NSMutableArray array];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(-10, 100)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(-8, 50)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(-6, 20)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(-4, 10)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(-2, 5)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(0, 0)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(2, 4)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(4, 16)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(6, 36)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(8, 64)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(10, 100)]];
self.scatterPlot = [[TUTSimpleScatterPlot alloc] initWithHostingView:_graphHostingView andData:data];
[self.scatterPlot initialisePlot];
}
Note that we’re storing our data as a simple array of CGPoints. However, as CGPoints aren’t descended from NSObject, we can’t store them in an NSArray directly, so we wrap them up in an NSValue object within the array.
Anyway, phew, it feels like a lot of work – but we’re finally there. The golden spot of any tutorial – the screenshot of the final product!
If you’re having any problems getting this to work in your own app, then please don’t hesitate to get in touch by leaving a comment below – I’ll do my best to reply in a timely manner. If you want to download the code from the sample project, you can do so using the link below.








Got your code to work, but cant get it to work with my data. Have you ever tried running in ARC? I believe thats the problem but the rest of my project is in ARC so i really don’t want to revert.
Hi Logan. I’m afraid I’ve not really made the move over to ARC yet, so I can’t give you a definitive answer. However, I’m under the impression that the process should be relatively simple – the first thing is to remove all calls to retain/release/autorelease in my code. I don’t call dealloc anywhere so apart from using the new layout for the main method (using @autoreleasepool instead of NSAutoReleasePool).
This page is a good read (and it should only take a few minutes to get the relevant information out) – http://longweekendmobile.com/2011/09/07/objc-automatic-reference-counting-in-xcode-explained/
From the above link, I get the impression that there is an entry in the ‘Edit’ menu for the file to convert it to ARC code. You can scroll down to ‘Migrating Existing Projects to ARC’ in the above link to see an example. I’d love to hear how you get on with using this code with ARC. If I get a chance, I’ll try to make an ARC version of the sample project at some point.
Hello! Thanks for the great tutorial! I’m still trying to get it to work but I keep having many errors on the CorePlotHeaders.
I get this on many headers.
/Users/iHiveMind/Desktop/Core Plot Introduction/Libs/CorePlot/CorePlotHeaders/CPTLayerAnnotation.h:15:1: error: property attributes ‘assign’ and ‘weak’ are mutually exclusive [3]
do you know what is causing this?
thanks!
Hi Miguel. Thanks for the positive feedback and for leaving a comment. Sorry to hear that you’re having problems getting the CorePlotHeaders to work in your app.
I suspect that you are trying to compile your project with ARC – Automatic Reference Counting enabled. Unfortunately, we’re currently in a transitional period where many libraries were written before ARC was available. When in ARC mode the compiler doesn’t allow certain definitions that were allowed before hand. The quick and easy solution is to turn off ARC for your project – obviously, that’s fine if you’re just learning how to use CorePlot and testing the system, but if you already have an app working – you might want to try the following (sourced from ARC and iOS5 – CorePlot).
1. If you are including CorePlot from source, you should be able to add it as a ‘sub-project’ to your main project. When CorePlot is enabled as a sub-project, you should be able to tell X-Code that the sub-project should be compiled without ARC.
2. In both cases (CorePlot from source and CorePlot from binaries) you will probably also need to update the header files that are firing errors. Changing the lines that start with @property (nonatomic, readwrite, assign) __weak to @property (nonatomic, readwrite, weak) might solve your problem.
If these don’t work – let me know, and I’ll see if I can suggest an alternative solution. Alternatively, it might be worth searching the web for “CorePlot and ARC”, as I suspect that CorePlot is popular enough that someone will have solved this and posted a message somewhere on a forum / discussion group.
Thank you so much! I have been able to get rid of those errors caused by the ARC, but the transition to xcode 4.3 is killing me… I started learning about iPhone programming like 10 days before the switch and now with the addition of ARC and Storyboards I’m absolutely confused LOL
I’m trying to do the tutorial with Storyboards and ARC as the project I’m going to use CorePlot with uses both, but still no luck making it work… I’ve compared our codes, and I believe the key is on the AppDelegate files but still my project compiles, runs but the UIView doesn’t appear… on the simulator only the label appears (and _graphViewController is indeed hooked with the ViewController)
I’ll keep trying, let’s see if I figure it out!
Hi,
I try to run this example in iPod 3 (ios4). But it’s not working, when i push the start button in newest Xcode, the application simply quit with error code 0. I set the the application setting acordingly(ios4, armv6 armv7), and i try another macbook.
Do you have idea what is my problem ?
On the iPod 4 working correctly.
Hi Volter. You will need to ensure that the project’s Deployment Target (in the Target Summary) is compiled to a low enough version for the OS on your iPod Touch. It might be that your project is being compiled for iOS 4.3 and your iPod Touch is only running 4.2 for instance. If your deployment target is 4.0 then it should work for any device running iOS 4.0 or onwards.
It’s also worth checking to ensure that you built the code using the required linker flags (-Objc and -all_load). I suspect that you have, if it works on the later devices, but I think I heard somewhere that it will work without -all_load on newer devices, but perhaps not older ones.
I’m afraid that I don’t know off the top of my head what the minimum version of iOS that is supported by the CorePlot framework. If the above suggestions don’t resolve your problem, then it might be worth searching the CorePlot discussion archive to see if you can find out what the minimum supported version of iOS is for CorePlot. It might be referencing a method that simply does not exist on older versions of iOS.
Hope this helps, and thanks for leaving a comment.
Hi John, I got your code to work fine in my app on the simulator. When I try to install my app on my iPhone (4S with iOS 5), I get this error:
ld: warning: ignoring file /Users/john/Documents/My Projects/Xcode/Project/ProjectiPhone/libCorePlot-CocoaTouch.a, file was built for archive which is not the architecture being linked (armv7)
I wonder if that means that CorePlot doesn’t support the 4S?
Hi John. My gut instinct from that message is that the CorePlot static library is built for armv6 instead of armv7. There are a few things that you could try here.
1. Set your project to run armv6 code instead of armv7. All iOS devices run arm6 whereas only newer ones will run arm7. This does however mean that you’re limiting yourself to an older architecture – so you’ll be losing out on some of the optimisations of the later arm chipsets. This is the quickest solution, and might be viable if you’re not running an intensive application and/or if you just want to get it working in the short term to test it out.
2. The full source code for CorePlot should be available from the CorePlot site. You can either include CorePlot as a sub-project of your project and have all of the source code included, or you could open the project separately and rebuild CorePlot so that the libCorePlot-CocoaTouch.a library includes both armv6 and armv7 architectures in the target’s Build Settings.
Let me know how you get on with these. Both solutions are off the top of my head without really looking into it, so if they don’t help – I’ll take a look at the project again and see what I can find out!
Ah ha, that worked. I did the second part of your suggestion #2. I opened the sample project “CPTTestApp-iPhone” that comes with CorePlot. Inside that, there’s something that looks like a another project called “CorePlot-CocoaTouch.xcodeproj”. That one has 3 “Targets” – one of which has an icon that looks like a red bull’s eye and it’s called “Universal Library”. It did have “armv7″ in the “ARCHS” part of “Build Settings”. So, I assumed that that was a good thing.
With the main project open, I changed the “Scheme” at the top in XCode 4.2 to point to my device, and ran it. I noticed that a new “libCorePlot-CocoaTouch.a” file then mysteriously got built (I had run that sample in the simulator before, but it didn’t build that file).
I copied the new .a file that was newly built into my project… and viola – works on my phone. However, all these crazy settings in XCode make me highly concerned about my ability to get my project successfully launched in the app store and have it run all the devices I want. I have no idea what most of these settings are. Most of my experience is in .NET where stuff “just works” (well, mostly).
Anyway, thanks for the help!
Hello!
Thankss to your tutorial I’m getting much more skilled at plotting but right I was trying to adapt the tutorial here to plot several graphs on the same plot space with no luck so far.
for 2 plots, I’d need to set to different plots like:
CPTScatterPlot *plot = [[[CPTScatterPlot alloc] init] autorelease];
plot.dataSource = self;
plot.identifier = @”mainplot”;
plot.dataLineStyle = lineStyle;
plot.plotSymbol = plotSymbol;
[self.graph addPlot:plot];
CPTScatterPlot *plot2 = [[[CPTScatterPlot alloc] init] autorelease];
plot2.dataSource = self;
plot2.identifier = @”otherplot”;
plot2.dataLineStyle = lineStyle;
plot2.plotSymbol = plotSymbol;
[self.graph addPlot:plot2];
———————————————–
alternatively ‘d need two sets of data
NSMutableArray *data1 = [NSMutableArray array];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(-10, 100)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(-8, 50)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(-6, 20)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(-4, 10)]];
NSMutableArray *data2 = [NSMutableArray array];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(0, 0)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(2, 4)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(4, 16)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(6, 36)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(8, 64)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(10, 100)]];
but how do you initiliase the plot using both data sets? should I modify the delegate methods somehow?
thank you!
Hi Miguel! It looks as though you’re very close (if you’re not already solved this already).
First of all – just to be clear. Adding two ‘plots’ in the way you are describing to a single graph will essentially allow you to draw 2 lines on the same chart / same pair of axis. If this isn’t what you’re after – then I apologise!
So, the first piece of code that you’ve posted above seems correct. That should add two lines to a single graph. You’ve done the right thing of setting the plot.identifier and the plot2.identifier to 2 unique strings. This means that both plots can use the same datasource / delegate and you can simply return the data required depending on which plot is passed to the datasource method.
Just a quick note, the second piece of code you’ve posted does have an error which could be causing you not to see the results you’re expecting. You have created 2 arrays ‘data1′ and ‘data2′. However, you are adding all of your data values to the array called ‘data’. It’s and easy slip up – but naturally you should have a number of [data1 addObject:...] calls and a bunch of [data2 addObject:...] calls.
Now that you have 2 plots and 2 sets of data, you simply need to ensure that your datasource delegate methods return the correct information for each plot. Essentially, you would want something like the following;
-(NSUInteger)numberOfRecordsForPlot:(CPTP *)plot { if ( [plot.identifier isEqualToString:@"mainplot"] ) { return [self.data1 count]; } if ( [plot.identifier isEqualToString:@"otherplot"] ) { return [self.data2 count]; } return 0; } -(NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index { if ( [plot.identifier isEqualToString:@"mainplot"] ) { return [...] } if ( [plot.identifier isEqualToString:@"otherplot"] ) { return [...] } return [NSNumber numberWithFloat:0]; }Where the [...] bits above are essentially the same as in the example, but referring to the relevant data array. Hope this helps. If other problems arise, or this isn’t relevant – just let me know and I’ll see what I can do!
Thanks for dropping by and leaving your message.
Hello John and Miguel,
I was also trying this…i’m stuck on how to initialise the scatter plot with the data sources.
In the view controller we declare 2 arrays, yet when we initialise the plot, we have to pass a single data source which will be assigned to graphData. This is used as the data source for the numberOfRecordsForPlot and numberForPlot methods.
As such, how do we pass the 2 arrays to the scatter plot instance when we initialise it. Below is the code for the arrays
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSMutableArray *data = [NSMutableArray array];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(2, 4)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(4, 16)]];
[data addObject:[NSValue valueWithCGPoint:CGPointMake(6, 36)]];
NSMutableArray *data2 = [NSMutableArray array];
[data2 addObject:[NSValue valueWithCGPoint:CGPointMake(0, 100)]];
[data2 addObject:[NSValue valueWithCGPoint:CGPointMake(2, 80)]];
[data2 addObject:[NSValue valueWithCGPoint:CGPointMake(4, 60)]];
self.scatterPlot = [[TUTSimpleScatterPlot alloc] initWithHostingView:_graphHostingView andData:data];
[self.scatterPlot initialisePlot];
}
I’m not sure how we will pass the 2 data sources.
Thanks.
I have done exactly what you have said to do, however, I am making this graph on the second tab of a tab bar application. I can get the Graph Hosting View to link with the View Controller. When I say this I mean I cannot get the _graphHostingView to be an option to select. All of my classes are identical to yours. I am wondering if this problem has to do with the fact that I am creating a tabbed view. If it is could you tell me how to fix this. Thanks.
Hi Sam, there are a few things to check here – but it sounds like the Interface Builder is not picking up that your second view controller is of the type ‘TUTViewController’ instead of just a standard view controller (so it doesn’t see the IBOutlet variable that you’ve defined in your custom view controller).
I’m assuming that you’ve setup a Tabbed Application from the Tabbed App template that comes with XCode 4, and added TUTViewController (or something similar) to your project instead of using the default ‘SecondViewController’ class that comes with the app.
1. Ensure that you have definitely used IBOutlet in your definition of the _graphHostingView variable so it reads; “IBOutlet CPTGraphHostingView *_graphHostingView;”.
2. In your SecondViewController.xib or TUTViewController.xib file (whichever method you are using), make sure that the class of the File Owner is set to TUTViewController. To do this open up the .xib file, and select ‘File’s Owner’ on the left hand side. Then pick the third tab on the right hand side and you should see a ‘Custom Class’ section. Type in ‘TUTViewController’ in as the class name for the File’s Owner. This tells Interface Builder what type of class will be the View Controller for this view when loaded. Then you should be able to right click on the File’s Owner item and see _graphHostingView.
Hope one of these suggestions helps. If you’re still running into issues, please feel free to drop another message and I’ll help in any way that I can. Best of luck with your project.
What you said was correct. The .xib file did not have to correct owner. Thanks so much. I am saving this tutorial for later use by fellow students in our computer research class!
Great to hear that you got the sample working in the end. All the best with your future iPhone projects!
Set up per the instructions above in an existing project, getting the errors per below. In the XIB file, I set the UIView custom class to CPTGraphHostingView and hooked it up to the referencing outlet graphHostingView
2011-12-13 20:06:35.257 iEHR_2_0[19255:10703] Unknown class CPTGraphHostingView in Interface Builder file.
2011-12-13 20:06:35.266 iEHR_2_0[19255:10703] -[UIView setHostedGraph:]: unrecognized selector sent to instance 0x75cb8d0
2011-12-13 20:06:35.267 iEHR_2_0[19255:10703] *** Terminating app due to uncaught exception ‘NSInvalidArgumentException’, reason: ‘-[UIView setHostedGraph:]: unrecognized selector sent to instance 0x75cb8d0′
Hi Dave, it appears as though your app can’t find the definition of the class CPTGraphHostingView when it attempts to build the classes that are required to drive the UI. I suspect that your app defaults to creating that element as a UIView instead – which is why setHostedGraph doesn’t exist at runtime.
As CPTGraphHostingView is a CorePlot class, my first instinct is that you might have missed a step with regards to ensuring that the CorePlot libraries are all built and/or linked with your app. Assuming that you’re using the pre-built library, then you will need to ensure that;
1. The file libCorePlot-CocoaTouch.a exists within your project structure.
2. You have linked your app with both libCorePlot-CocoaTouch.a and QuarzCore.framework. You can see this by selecting your root project file in Xcode and selecting the ‘Build Phases’ tab. Both of these entries should appear in the ‘Link Binary with Libraries’ section of this tab-pane.
3. You might also need to ensure that you are using the ‘-Objc -all_load’ liner flags in your project. Instructions for this are displayed above in ‘Step 2′ of the tutorial, but you can find the option for ‘Other Linker Flags’ in the ‘Build Settings’ tab of your project screen.
Thanks for dropping by and I hope this helps in your case. All the best with any projects that you are working on.
I went through the steps you outlined. I adjusted the ‘Other Linker Flags’ per above. Otherwise everything appears to be configured as per you described, yet I am still getting the error. Does CPTGraphHostingView.h need a corresponding .m file?
Great article John. Thanks very much, You saved my day.
Q: How can I tell Core plot only to take certain part of the screen to do its rendering ?
I have defined something like the following, which works fine, but once I add some UILabel or UIButton controls on top of this area, I see the text upside down, which tells me I am still in Core Plot coordinate system.
graph = [[CPTXYGraph alloc] initWithFrame: CGRectMake(0, 0, 320, 240)];
CPTGraphHostingView *hostingView = (CPTGraphHostingView *)self.view;
hostingView.hostedGraph = graph;
hostingView.backgroundColor = [UIColor blackColor];
graph.paddingLeft = 2.0;
graph.paddingTop = 100.0;
graph.paddingRight = 2.0;
graph.paddingBottom = 2.0;
Thanks
Hi Volek, great to hear that you found the tutorial helpful.
With regards to labels being displayed upside down when they are placed above the CPTGraphHostingView – I think the problem here is that core plot does something strange under the hood. I get the impression that it renders everything in it’s own co-ordinate system and then transforms the view (and all of it’s children) within the view hierarchy.
The simple solution is to ensure that the views that you wish to display on top of the hosting view aren’t actually subviews of the hosting view. This is a bit fiddly, but in the object browser on the left hand side of the interface builder you can see whether your UILabel is ‘nested inside’ (ie. a child of) the hosting view or whether it’s on the same level as the hosting view. The problem is, when you drag and drop the elements – it assumes you want to add it as a subview, so you either need to drop it outside of the hosting view and move it with the keyboard / entering co-ordinates manually or by moving it around in the object hierarchy after you have placed it.
One more thing to watch out for – you will have to ensure that your label is stacked above the hosting view. As it would no longer be a child of the hosting view, you will need to ensure that it appears above (or it could actually be below, as it might build it top-to-bottom) of the hosting view in the object hierarchy.
Hope this helps. Good luck with your CorePlot endeavours!
Getting closer. Got the issue with IB corrected. When I open up my UIView with embedded GraphHostingView, get a blank screen. Compiler states “SimpleScatterPlot: Cannot initialise plot without hosting view or data” Any suggestions appreciated.
Dave, how did you get the IB issue corrected? I too, am getting the same message “Unknown class CPTGraphHostingView…”. Other than that, everything else seems fine but that crashes my app.
Hi Dave, I’ve been trying to give some thought as to why this isn’t working and I’m wondering if your project is using ARC. I aim to upgrade the attached project over the XMas break so that it’s ARC compatible, but as I’ve not made the move to ARC yet – that’s not something I can do without at least a little bit of reading into it before hand.
Would love to hear about how you fixed the IB Issue. I’ll edit the post above with some ‘tips and tricks’ based on the comments section – as there are quite a few comments to read through now for someone looking for a fix.
If you’re still having problems, feel free to send a zip of the project over to me (assuming it’s just a test project, and not an actual working / commercial project) and I’ll see if I can figure it out. Sometimes I just need the debugger to figure out what the problem is! My email is john -AT- johnwordsworth.com.
Hi,
Great sample app!
I saw that the below line was in the code I downloaded in the TUTSimpleScatterPlot.h file. I believe this may need to be deleted:
+(TUTSimpleScatterPlot *)plotWithHostingView:(CPTGraphHostingView *)hostingView andData:(NSMutableArray *)data;
Xcode 4.2 complained about this line. Once deleted, it no longer complained.
Thanks!
Hi Dave, thanks for the comment and for raising this issue. I’ll have a look through the code over the Christmas holiday and update it.
Just to confirm – are you use ARC? I’ve not yet made the move across to ARC and I’m aware that there are some changes required to make it work properly with ARC. I aim to put up an alternative version of the code over the XMas break and will include your fix at that time.
Thanks for the comment and the kind words. All the best with your CorePlot endeavours!
ARC was Off.
Just re-read the tutorial – it is GREAT! Thanks for this wonderful deciphering of CorePlot.
Hi Dave. Hehe – I’ve only just started using ARC in some of my projects. It’s like driving an automatic car for the first time I guess – it takes a while to trust it when you’ve been doing it the old way for so long! I’ll hopefully have an ARC compatible version of this tutorial available for Cocoa soon, which means I’ll fully understand the differences between having it turned on and off with CorePlot.
Thanks for the comments. Wishing you all the best with your CorePlot endeavours.
hey ,
i have followed exactly the steps mentioned in the tutorial! build is successful but i get no output. whenever i get inside the project it comes back to the main menu without displaying the graph! can you please let me know what the problem is?
I have managed to get the example scatter graph from coreplot working on project having ARC enabled.
I am not sure if its the right way, but coreplot framework built without errors when removing all __weak attributes to variables.
You can find the project with sample graph and converted coreplot framework embedded in project here: https://github.com/praveenr019/CorePlot-Simple-Graph
John this is a great guide and the first one that has worked following start to finish for me, appreciate your help effort with this!
Thanks for the positive feedback Glen and I’m glad that it all worked for you out of the box! Best of luck with any projects you are working on using CorePlot.
Thank you so so much!!!! This tutorial is awesome and works.
John we can’t thank you enough for this *great* tutorial!
Do you mind posting briefly those minor changes you mention
for the desktop version?
Please forgive my ignorance, but I can’t find a way to do it..
Thanks in advanced for your time & your help!
Hi Simos,
First of all, many thanks for your kind comment. Apologies that it has taken me so long to get back to you – things have just been crazy busy here lately and I’ve not had enough time to keep on top of everything.
I’m afraid it’s been a few weeks since I’ve been near CorePlot so I can’t give you an answer off the top of my head. However, I’m actually planning on doing a separate tutorial on using CorePlot with Cocoa. I was going to do as you suggested upon reading your comment a couple of weeks ago, but I figured this would be a good opportunity to not only show how to use CorePlot with Cocoa, but also with ARC enabled and with a different plot type. I figured 3 levels of variation would make for a nice, second tutorial that is similar enough that it can help to reinforce this one while being different enough to be helpful.
Please check back in a week or two when I hope to have a Cocoa version of a tutorial similar to this available. Thanks again for stopping by and leaving a comment. All the best.
Hi, i was trying to make a histogram with core plot..can u help me with box x axis and y-axis are in an array form.
thanks in advance
John, thanks a lot for this tutorial, Core-Plot is not as simple as a walk in the park, and the whole concept is a lot more clear to me now and seems attainable thanks to you.
Pleas keep up the good work.
Hi Carlos, thanks for the kind words. You’re right there – it took me a long time to get my head around CorePlot the first time I used it. While very powerful, there is a steep initial learning curve.
Hope that everything goes well with your CorePlot project(s) in the future. Thanks again for the comment.
Hey John!
Awesome tutorial. You saved a weeks time with this page. Thanks a lot. Is it possible to add in two graphs – ( Dual X-Y axis on the same page ) ?
What if the data keeps being pushed in for every second will it be able to plot live ?
Hey John, what a nice tutorial!! I’ve been looking for something like this through a couple of weeks ago… the core-plot documentation isn’t good and a “intro class” like yours was what I needed to understand how it works… However (there’s always a “However”) i’m getting the following error when I try to run my app:
‘NSInvalidArgumentException’, reason: ‘-[CPIViewController setScatterPlot:]: unrecognized selector sent to instance 0x6eb39b0′
Obs.: My Project Prefix Starts with “CPI”.
Any idea?
Thanks!
Just found out what was going on! I forgot de @synthesize statement to the scatterPlot attribute.
I’m just a noob! =)
Hi Vini, glad to see that you’ve solved your problem! Unfortunately, I’m just catching up on blog comments today so I didn’t get a chance to offer any suggestions before you found the solution! Whether you’re new to coding or not, the simple things will always catch you out once in a while! Haha.
All the best with your CorePlot endeavours and projects. Thanks for stopping by and commenting.
I have a question about how to move your x-axis label up the graph. For instance, my y-axis has a break and starts at 15.5 I run into a problem with the x-axis now because it is not showing on the graph. How do I get the x-axis to move up?
Hi,
i am making a scatter plot, in my graph i want the grid lines at the back of gray and legend..
Can u let me know how to do that.
Thanks.
Hi BlueZone, I’m afraid I’m a bit out of practice with CorePlot at the moment. However, I need to return to it early next week, so I will hopefully have an answer for you then if you’ve not figured it out before hand! Sorry I don’t have a better answer for you now, I just wanted you to know that I’ve read your comment and will be in a much better place to provide a useful answer in a week’s time or so.
If you figure it out before then, please drop by with a short comment on what you found worked! All the best. JW
Hi John,
Thanks for the useful tutorial. I have able to draw graph and grid lines by following your tutorial. But i am trying to add plot point when i touch a point in Graph. I have tried with on stack overflow post
-(BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDownEvent:(id)event atPoint:(CGPoint)point
{
CPTScatterPlot *plot = (CPTScatterPlot*)[[self.cptGraph allPlots]objectAtIndex:0];
CGPoint pointInPlotArea = [plot convertPoint:point toLayer:plot];
if ([plot containsPoint:pointInPlotArea]) {
NSDecimal touchDataPoint[2];
[space plotPoint:touchDataPoint forPlotAreaViewPoint:CGPointMake(4,4)];
}
return true;
}
the delegate method gets invoked when i touch the graph, but the plotting point is not drawn. help me to do that.
Hi….I have initialized two arrays with data to plot two on the same scatter plot, but i’m not able to present it. I am only able to plot only any one of the arrays data but not both at the sam etime.
Kindly help me out with this.
Hi John,
I am trying to add plotting point, when i touch a graph.I did that using CPTPlotSpace delegate method . But when i press in (2,4) coordinate it points to (2.5,5) like that. I am not able to add the plotting point in exact touch position.How to achieve this?
[...] a custom bar chart with Core Plot (Note : If you want to create only a simple line graph, then John Wordsworth’s tutorial is the place to [...]
[...] Adding charts to your iPhone / iPad App using Core Plot 0.9 [...]
[...] I found only one tutorial that uses the last Xcode. You can find it in Dr John Wordsworth site and specifically here. [...]