Pages

Subscribe:

Monday, March 28, 2011

iPhone SDK - How to Draw Bar charts using Coreplot?

How to Draw Bar charts using Coreplot?


Here is an example which uses coreplot library to draw bar charts for sales figures of a year.


Screen Shots






1. First, add the core plot to a new project. 


First, drag the CorePlot-CocoaTouch.xcodeproj file into your iPhone application's Xcode project (making sure that nothing's copied and the paths are relative to your project). Then go to the Targets tab in Xcode, select your application's target, and bring up the inspector. Go to the General settings page and add the CorePlot-CocoaTouch library as a direct dependency.
Core Plot is built as a static library for iPhone, so you'll need to drag the libCorePlot-CocoaTouch.a static library from under the CorePlot-CocoaTouch.xcodeproj group to your target's Link Binary With Libraries folder.
You'll also need to point to the right header location. Under your Build settings, set the Header Search Paths to the relative path from your application to the framework/ subdirectory within the Core Plot source tree. Make sure to make this header search path recursive. You need to add -ObjC and -all_load to Other Linker Flags as well.
Core Plot is based on Core Animation, so if you haven't already, add the QuartzCore framework to your application project.
Finally, you should be able to import all of the Core Plot classes and data types by inserting the following line in the appropriate source files within your project:
#import "CorePlot-CocoaTouch.h"


2. Modify the app delegate as below. Add arrays to keep the month and sales data.

//
//  BarChartAppDelegate.h
//  BarChart
//
//  Created by Raja T S Sekhar on 3/26/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <UIKit/UIKit.h>

@class BarChartViewController;

@interface BarChartAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    BarChartViewController *viewController;
UINavigationController *navController;
NSMutableArray *months;
NSMutableArray *sales;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet BarChartViewController *viewController;

@property (nonatomic, retain) NSMutableArray *months;
@property (nonatomic, assign) NSMutableArray *sales;

@end


Here is the app delegate implementation.

//
//  BarChartAppDelegate.m
//  BarChart
//
//  Created by Raja T S Sekhar on 3/26/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "BarChartAppDelegate.h"
#import "BarChartViewController.h"

@implementation BarChartAppDelegate

@synthesize window;
@synthesize viewController;

@synthesize months;
@synthesize sales;

#pragma mark -
#pragma mark Application lifecycle

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    
    // Override point for customization after application launch.
months = [[NSMutableArray alloc] initWithObjects:@"January",@"February",@"March",@"April",@"May",@"June",
  @"July",@"August",@"September",@"October",@"November",@"December",nil];
sales = [[NSMutableArray alloc] initWithObjects:@"35",@"13",@"12",@"23",@"34",@"34",@"56",@"67",@"23",@"11",@"32",@"43",nil];
// Override point for customization after application launch.
navController = [[UINavigationController alloc] init];
BarChartViewController *barChartViewController = [[BarChartViewController alloc] init];
navController.navigationBar.hidden = NO;
//Push the first view controller onto the view controller stack without animation
[navController pushViewController:barChartViewController animated:NO];
//Some memory management
[barChartViewController release];
    // Add the view controller's view to the window and display.
  [self.window addSubview:navController.view];
    // Add the view controller's view to the window and display.
    //[self.window addSubview:viewController.view];
    [self.window makeKeyAndVisible];

    return YES;
}


- (void)applicationWillResignActive:(UIApplication *)application {
    /*
     Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
     Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
     */
}


- (void)applicationDidEnterBackground:(UIApplication *)application {
    /*
     Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
     If your application supports background execution, called instead of applicationWillTerminate: when the user quits.
     */
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
    /*
     Called as part of  transition from the background to the inactive state: here you can undo many of the changes made on entering the background.
     */
}


- (void)applicationDidBecomeActive:(UIApplication *)application {
    /*
     Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
     */
}


- (void)applicationWillTerminate:(UIApplication *)application {
    /*
     Called when the application is about to terminate.
     See also applicationDidEnterBackground:.
     */
}


#pragma mark -
#pragma mark Memory management

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
    /*
     Free up as much memory as possible by purging cached data objects that can be recreated (or reloaded from disk) later.
     */
}


- (void)dealloc {
    [viewController release];
    [window release];
    [super dealloc];
}


@end


Now, we will be designing the data entry screen. Modify the controller as below.

//
//  BarChartViewController.h
//  BarChart
//
//  Created by Raja T S Sekhar on 3/26/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <UIKit/UIKit.h>

#import "ChartViewController.h"
#import "BarChartAppDelegate.h";
@class BarChartAppDelegate;

@interface BarChartViewController : UIViewController <UIPickerViewDelegate, UIActionSheetDelegate, UITableViewDelegate> {
BarChartAppDelegate *barChartAppDelegate;
int selectedMonth;
UIActionSheet *monthAction;
UIPickerView *monthPicker;
IBOutlet UITableView *salesTable;
IBOutlet UITextField *monthName;
IBOutlet UITextField *salesAmount;
}

@property (nonatomic, retain) BarChartAppDelegate *barChartAppDelegate;
@property (nonatomic, retain) UITableView *salesTable;
@property (nonatomic, retain) UITextField *monthName;
@property (nonatomic, retain) UITextField *salesAmount;

- (IBAction) chartIt;
- (IBAction) updateSales;
- (IBAction) showMonthPicker;

@end


Here is the implementation for the controller.

//
//  BarChartViewController.m
//  BarChart
//
//  Created by Raja T S Sekhar on 3/26/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "BarChartViewController.h"

@implementation BarChartViewController

@synthesize monthName;
@synthesize salesAmount;
@synthesize salesTable;
@synthesize barChartAppDelegate;

/*
// The designated initializer. Override to perform setup that is required before the view is loaded.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}
*/

/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
}
*/



// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
self.title = @"Bar Chart Example";
barChartAppDelegate = (BarChartAppDelegate *) [[UIApplication sharedApplication] delegate];
salesTable.delegate = self;
    [super viewDidLoad];
}


- (IBAction) updateSales {

if ([[monthName text] isEqualToString:@""] || [[salesAmount text] isEqualToString:@""]) {
UIAlertView *validationAlert = [[UIAlertView alloc] initWithTitle:@"Bar Chart" 
  message:@"Please select Month and type sales amount" 
delegate:self 
cancelButtonTitle:nil 
otherButtonTitles:@"OK", nil];
[validationAlert show];
[validationAlert release];
return ;
}
[barChartAppDelegate.sales replaceObjectAtIndex:selectedMonth withObject:[salesAmount text]];
[salesAmount setText:@""];
[monthName setText:@""];
[salesTable reloadData];
}

- (IBAction) chartIt {
ChartViewController *chartViewController = [[ChartViewController alloc] init];
[self.navigationController pushViewController:chartViewController animated:YES];
}

// Table delegates
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [barChartAppDelegate.months count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    }
NSString *cellValue = [NSString stringWithFormat:@"%@ - %@",
  [barChartAppDelegate.months objectAtIndex:indexPath.row],[barChartAppDelegate.sales objectAtIndex:indexPath.row]];
cell.textLabel.text = cellValue;
    return cell;
}

- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 25;
}


- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
cell.backgroundColor = [UIColor lightGrayColor];
}


- (IBAction) showMonthPicker {
monthAction = [[UIActionSheet alloc]
  initWithTitle:@"Month"
  delegate:self
  cancelButtonTitle:nil
  destructiveButtonTitle:nil
  otherButtonTitles:nil];
UIToolbar *pickerDateToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
pickerDateToolbar.barStyle = UIBarStyleBlackOpaque;
[pickerDateToolbar sizeToFit];
NSMutableArray *barItems = [[NSMutableArray alloc] init];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[barItems addObject:flexSpace];
UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(pickerDoneClick)];
[barItems addObject:doneBtn];
[pickerDateToolbar setItems:barItems animated:YES];
[monthAction addSubview:pickerDateToolbar];
monthPicker = [[UIPickerView alloc]initWithFrame:CGRectMake(0, 44.0, 320, 44)];
monthPicker.delegate = self;
monthPicker.showsSelectionIndicator = YES;

[monthAction addSubview:monthPicker];
[monthAction showInView:self.view];
[monthAction setBounds:CGRectMake(0,0, 320,464)];
[pickerDateToolbar release];
[barItems release];
[flexSpace release];
[doneBtn release];
}

- (void)pickerDoneClick {
[monthAction dismissWithClickedButtonIndex:0 animated:YES];
[monthAction release];
selectedMonth = [monthPicker selectedRowInComponent:0]; 
[monthName setText:[barChartAppDelegate.months objectAtIndex:selectedMonth]];
}

- (BOOL) textFieldShouldReturn:(UITextField *)theTextField { 
[theTextField resignFirstResponder]; 
return YES
}

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView {
return 1;
}

- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component {
return [barChartAppDelegate.months count];
}

- (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [barChartAppDelegate.months objectAtIndex:row];
}

- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
}


/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/

- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}


- (void)dealloc {
[barChartAppDelegate release];
[monthAction release];
[monthPicker release];
[salesTable   release];
[monthName release];
[salesAmount release];
    [super dealloc];
}

@end


Now, add one more view controller to draw the chart.

//
//  ChartViewController.h
//  BarChart
//
//  Created by Raja T S Sekhar on 3/28/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "CorePlot-CocoaTouch.h"

#import "BarChartAppDelegate.h";

@class BarChartAppDelegate;

@interface ChartViewController : UIViewController <CPPlotDataSource> {
BarChartAppDelegate *barChartAppDelegate;
NSMutableArray *samples;
}
@property (nonatomic, retain) BarChartAppDelegate *barChartAppDelegate;

- (void) drawBarChart;
-(void) generateDataSamples;

@end

Here is the implementation.

//
//  ChartViewController.m
//  BarChart
//
//  Created by Raja T S Sekhar on 3/28/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "ChartViewController.h"

#define X_VAL @"X_VAL"
#define Y_VAL @"Y_VAL"

@implementation ChartViewController

@synthesize barChartAppDelegate;


// The designated initializer.  Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
/*
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization.
    }
    return self;
}
*/


// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
self.title = @"Bar Chart";
barChartAppDelegate = (BarChartAppDelegate *) [[UIApplication sharedApplication] delegate];
[self drawBarChart];
    [super viewDidLoad];
}

- (void) drawBarChart {
[self generateDataSamples];
double xAxisStart = 0;
double xAxisLength = [samples count];
double yAxisStart = 0;
double yAxisLength = [[samples valueForKeyPath:@"@max.Y_VAL"] doubleValue];
CPGraphHostingView *hostingView = [[CPGraphHostingView alloc] initWithFrame:CGRectMake(0, 0, 320, 420)];
[self.view addSubview:hostingView];
CPXYGraph *graph = [[CPXYGraph alloc] initWithFrame:self.view.bounds];
hostingView.hostedGraph = graph;
CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromDouble(xAxisStart)
  length:CPDecimalFromDouble(xAxisLength+1)];
plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromDouble(yAxisStart)
  length:CPDecimalFromDouble(yAxisLength)];
CPBarPlot *plot = [[CPBarPlot alloc] initWithFrame:CGRectZero];
plot.plotRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromDouble(0.0)
length:CPDecimalFromDouble(xAxisLength)];
plot.barOffset = 1.0;
plot.dataSource = self;
[graph addPlot:plot];
[plot release];
[graph release];
[hostingView release];
}

-(void) generateDataSamples {
int numSamples = [barChartAppDelegate.sales count];

samples = [[NSMutableArray alloc] initWithCapacity:numSamples];
for (int i = 0; i < numSamples; i++){
double x = i;
double y = [[barChartAppDelegate.sales objectAtIndex:i] doubleValue];
NSDictionary *sample = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithDouble:x],X_VAL,
[NSNumber numberWithDouble:y],Y_VAL,
nil];
[samples addObject:sample];
}
}

-(NSUInteger)numberOfRecordsForPlot:(CPPlot *)plot;  {
return [samples count];
}

-(NSNumber *)numberForPlot:(CPPlot *)plot field:(NSUInteger)fieldEnum 
  recordIndex:(NSUInteger)index; {
NSDictionary *sample = [samples objectAtIndex:index];
if (fieldEnum == CPScatterPlotFieldX)
return [sample valueForKey:X_VAL];
else
return [sample valueForKey:Y_VAL];
}



/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations.
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc. that aren't in use.
}

- (void)viewDidUnload {
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}


- (void)dealloc {
[samples release];
    [super dealloc];
}


@end

Connect the IBOutlets and IBActions in the views. Run the application.

Happy Coding with iPhone SDK!!!