Bar/histogram on core-plot

I’m playing with core-plot recently :) I wanted to draw a bar plot such as this,

The first problem I had was the default settings gave me bars that are too thin. I wanted bars that are bit wider but not too wide. The slimmest bars would be strips of lines. The widest bars would take up a full slot, for example one bar would start on the 0 mark on the x-axis and end on the 1 mark, the other starts on 1 and ends on 2, etc. Wide bars may be interesting to some people though. I thought they make my graph look too cluttered so I decided that a reasonable width would be half of the distance between successive marks. My second requirement was the marks on the x-axis should divide bars into two equal halves (as shown). This post is how I managed to do this particular task. I don’t put a full code listing here but I’m happy to do so if anyone is interested. There is some code listing in my previous post about my first go on line plot. If you are serious about core-plot, having the API will be useful.

Create the bar plot first,

CPBarPlot* barPlot = [[[CPBarPlot alloc] initWithFrame:self.mGraph.defaultPlotSpace.graph.bounds] autorelease];
barPlot.identifier = @"MyBarPlot";
barPlot.dataSource = self;

To set the width of bars we need to first find the length of the x-axis the total length of the x-axis. The first line below takes the total width of the iphone screen, which is 320 (assuming portrait here). Graphs have left and right paddings. The second line subtracts those.


double spaceAvailableOnXaxis =320;
spaceAvailableOnXaxis = spaceAvailableOnXaxis - (self.mGraph.paddingLeft + self.mGraph.paddingRight);

We now set the width of a bar. Because each bar is half as wide as the distance between successive marks, the width would be the length of the x-axis divided by twice the number of marks. I put “4″ in the code below because my x-axis has four marks.


barPlot.barWidth = spaceAvailableOnXaxis/(4*2);

Next we need to tell core-plot where to start plotting the bars. The tricky part here is this should be specified in terms of bar width. So if you specify “3″, the first bar will be drawn at a distance (3*barwidth) from the beginning of the x-axis, and not at a distance of 3.0. In the graph shown above, I want the first bar to appear at mark 1 on the x-axis. As we said earlier, the distance between 0 and 1, which is the distance between successive marks, is twice the width of a bar. So we do,


barPlot.barOffset = 2;

We now have this graph

What gives? Before all this story started, I had to set the length of my x-axis. I did,

double xAxisLengthOriginal = 4.0; //ignore the "Original" part of the variable for now, it'll become clear soon
CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)self.mGraph.defaultPlotSpace;
plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0)
length:CPDecimalFromFloat(xAxisLengthOriginal)];

It is a good idea to consider the datapoints being plotted to decide how long the x-axis should be so as to make good use of the space available. But the the effect of leaving the length of the x-axis to the maximum value of our x-axis data (in this case 4.0) results in a graph that goes right up to the boundary. In the bar plot we have here it certainly is not desirable. Even in a line plot, the right most data point in this scenario will be too far to the right that it looks as if some part of the graph was cut out. One way of leaving slack space is to do as such,

double xAxisLengthOriginal = 4.0;
xAxisLength = xAxisLengthOriginal + 0.2*xAxisLengthOriginal

The result is this.

We have extra space as planned. But because the length of the x-axis has changed, it is messing up our previous calculation of the bar width. The bars are a little bit wider than we want them to be. So before setting the bar width we add the line,

spaceAvailableOnXaxis = spaceAvailableOnXaxis*(xAxisMaxOriginal/xAxisMax);

This reverses the effects of our earlier “slack space surgery”. So to summarize, prepare the plot space:

double xAxisLengthOriginal = 4.0;
xAxisLength = xAxisLengthOriginal + 0.2*xAxisLengthOriginal
CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)self.mGraph.defaultPlotSpace;
plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0)
length:CPDecimalFromFloat(xAxisLength)];
...

Then the bar plot:

CPBarPlot* barPlot = [[[CPBarPlot alloc] initWithFrame:self.mGraph.defaultPlotSpace.graph.bounds] autorelease];
barPlot.identifier = @"MyBarPlot";
barPlot.dataSource = self;
double spaceAvailableOnXaxis =320;
spaceAvailableOnXaxis = spaceAvailableOnXaxis - (self.mGraph.paddingLeft + self.mGraph.paddingRight);
spaceAvailableOnXaxis = spaceAvailableOnXaxis*(xAxisMaxOriginal/xAxisMax);
barPlot.barWidth = spaceAvailableOnXaxis/(4*2);
barPlot.barOffset = 2;

Hope this helps someone out there :)

UPDATE

For all those who asked, I’m dumping my .h and .m files below.


//

//  TaggedVideoGraphViewController.h

//  SwimmerApp

//

//  Created by tilaye on 1/22/10.

//  Copyright 2010 __MyCompanyName__. All rights reserved.

//
 


#import <UIKit/UIKit.h>

#import "CorePlot-CocoaTouch.h"

#import <MessageUI/MessageUI.h>

#import "AnalysisInformation.h"

#import "CommonProtocols.h"

#import "GraphTheme.h"


typedef enum GraphType {GRAPH_STROKE_COUNT, GRAPH_STROKE_FREQUENCY, GRAPH_TIME} GraphType;


@interface TaggedVideoGraphViewController : UIViewController <CPPlotDataSource, MFMailComposeViewControllerDelegate, UITextFieldDelegate, UITextViewDelegate, AnalysisView> {

 CPXYGraph* mGraph;

 GraphType mGraphType;

 NSMutableArray* mGraphDataArray;

 NSString* mGraphTitle;

 UIBarButtonItem* mEmailButton;

 AnalysisInformation* mAnalysisInfo;

 IBOutlet UIView* mParentForGraphView;

 CPLayerHostingView* mGraphView;

 IBOutlet UILabel* mLabelBackgroundText;

}


@property (nonatomic, assign) GraphType mGraphType;

@property (nonatomic, retain) CPXYGraph* mGraph;

@property (nonatomic, retain) NSMutableArray* mGraphDataArray;

@property (nonatomic, retain) NSString* mGraphTitle;

@property (nonatomic, retain) UIBarButtonItem* mEmailButton;

@property (nonatomic, retain) AnalysisInformation* mAnalysisInfo;

@property (nonatomic, retain) CPLayerHostingView* mGraphView;

@property (nonatomic, retain) UIView* mParentForGraphView;

@property (nonatomic, retain) UILabel* mLabelBackgroundText;


- (NSMutableArray*) extractGraphArrayFromArray:(NSMutableArray*) array OfType:(GraphType)graphType;

- (double) findMax:(NSMutableArray*)array ForX:(Boolean)forX;

- (IBAction) emailButtonPressed;

- (void) renderGraph;

- (void) setTitle:(GraphType)GraphType;

- (CPLayerHostingView*) createGraphWithTheme:(id<GraphTheme>) theme;


@end


</pre>

//

//  TaggedVideoGraphViewController.m

//  SwimmerApp

//

//  Created by tilaye on 1/22/10.

//  Copyright 2010 __MyCompanyName__. All rights reserved.

//


#import "TaggedVideoGraphViewController.h"

#import "CorePlot-CocoaTouch.h"

#import "GraphDataPoint.h"

#import "TagStatisticsInformationBlock.h"

#import <MessageUI/MessageUI.h>

#import "VideoTag.h"

#import "EventInfo.h"

#import "AppConstants.h"

#import "GraphTheme.h"

#import "GraphThemeDark.h"

#import "GraphThemeLight.h"


@implementation TaggedVideoGraphViewController


@synthesize mGraphType;

@synthesize mGraph;

@synthesize mGraphDataArray;

@synthesize mGraphTitle;

@synthesize mEmailButton;

@synthesize mAnalysisInfo;

@synthesize mGraphView;

@synthesize mParentForGraphView;

@synthesize mLabelBackgroundText;


NSString* X_AXIS_TITLE = @"Mark";

double IPHONE_SCREEN_WIDTH = 320.0;


/*

 // 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 {

 if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {

 // Custom initialization

 }

 return self;

}

*/


- (void) setAnalysisInformation:(AnalysisInformation*)AnalysisInfo {

 self.mAnalysisInfo = AnalysisInfo;

}

/*

// 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)viewDidLoad {

 [super viewDidLoad];


 [self.view addSubview:self.mAnalysisInfo.options];


 [self setTitle:self.mGraphType];


 self.mGraphDataArray = [self extractGraphArrayFromArray:self.mAnalysisInfo.tagStatistics OfType:mGraphType];


 if ([self.mGraphDataArray count] <= 1) {

 self.mLabelBackgroundText.numberOfLines = 0;

 self.mLabelBackgroundText.lineBreakMode = UILineBreakModeWordWrap;

 self.mLabelBackgroundText.text = GRAPH_BACKGROUND_TEXT;

 self.mLabelBackgroundText.hidden = NO;

 self.mAnalysisInfo.parentView.navigationItem.rightBarButtonItem = nil;

 }else {

 self.mLabelBackgroundText.hidden = YES;

 mEmailButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose target:self action:@selector(emailButtonPressed)];

 self.mAnalysisInfo.parentView.navigationItem.rightBarButtonItem = mEmailButton;

 [self renderGraph];

 }

}


- (void) setTitle:(GraphType)GraphType {

 switch (GraphType) {

 case GRAPH_STROKE_COUNT:

 self.mGraphTitle = @"Strokes";

 break;

 case GRAPH_STROKE_FREQUENCY:

 self.mGraphTitle = @"Stroke Frequency";

 break;

 case GRAPH_TIME:

 self.mGraphTitle = @"Time";

 break;

 default:

 break;

 }

}


- (void) renderGraph {

 id<GraphTheme> theme = [[GraphThemeDark alloc] init];

 self.mGraphView = [self createGraphWithTheme:theme];

 [self.mParentForGraphView addSubview:mGraphView];

}


- (CPLayerHostingView*) createGraphWithTheme:(id<GraphTheme>) theme {

 //create graph

 CPXYGraph* graph;

 CPLayerHostingView* graphView;


 graph = [[CPXYGraph alloc] initWithFrame:self.mParentForGraphView.bounds];


 //    self.mGraphView = [[CPLayerHostingView alloc]initWithFrame:[UIScreen mainScreen].bounds];

 graphView = [[CPLayerHostingView alloc]initWithFrame:self.mParentForGraphView.bounds];

 CPLayerHostingView *hostingView = (CPLayerHostingView *)graphView;

 hostingView.hostedLayer = graph;

 graph.paddingLeft = 55.0;

 graph.paddingTop = 20.0;

 graph.paddingRight = 15.0;

 graph.paddingBottom = 50.0;


 //set plot space

 double xAxisMaxOriginal = [self findMax:mGraphDataArray ForX:YES];

 double xAxisMax = [self findMax:mGraphDataArray ForX:YES];

 double yAxisMax = [self findMax:mGraphDataArray ForX:NO];

 //leave a small gap at the end so graph does go till the end

 xAxisMax = xAxisMax + 0.2*xAxisMax;

 yAxisMax = yAxisMax + 0.2*yAxisMax;


 CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;

 plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0)

 length:CPDecimalFromFloat(xAxisMax)];

 plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0)

 length:CPDecimalFromFloat(yAxisMax)];


 //set line style

 CPLineStyle *lineStyle = [CPLineStyle lineStyle];

 lineStyle.lineColor = [CPColor blackColor];

 lineStyle.lineWidth = 2.0f;


 //setup x-axis


 CPXYAxisSet *axisSet = (CPXYAxisSet *)graph.axisSet;

 axisSet.xAxis.majorIntervalLength = [[NSDecimalNumber decimalNumberWithString:@"1"] decimalValue];

 axisSet.xAxis.minorTicksPerInterval = 0;

 axisSet.xAxis.majorTickLineStyle = lineStyle;

 axisSet.xAxis.minorTickLineStyle = lineStyle;

 axisSet.xAxis.axisLineStyle = lineStyle;

 axisSet.xAxis.minorTickLength = 5.0f;

 axisSet.xAxis.majorTickLength = 7.0f;

 axisSet.xAxis.labelOffset = 3.0f;

 axisSet.xAxis.title = X_AXIS_TITLE;

 NSString *xAxisTitleLocation = [NSString stringWithFormat:@"%f", (xAxisMax/2)];

 axisSet.xAxis.titleLocation = [[NSDecimalNumber decimalNumberWithString:xAxisTitleLocation] decimalValue];;

 NSNumberFormatter* formatter = [[[NSNumberFormatter alloc] init] autorelease];

 [formatter setNumberStyle:NSNumberFormatterDecimalStyle];

 axisSet.xAxis.labelFormatter = formatter;


 //setup y-axis

 //    axisSet.yAxis.majorIntervalLength = [[NSDecimalNumber decimalNumberWithString:@"5"] decimalValue];

 NSString *yAxisMajorIntervalLength = [NSString stringWithFormat:@"%f", (yAxisMax/5)];

 axisSet.yAxis.majorIntervalLength = [[NSDecimalNumber decimalNumberWithString:yAxisMajorIntervalLength] decimalValue];

 axisSet.yAxis.minorTicksPerInterval = 4;

 axisSet.yAxis.majorTickLineStyle = lineStyle;

 axisSet.yAxis.minorTickLineStyle = lineStyle;

 axisSet.yAxis.axisLineStyle = lineStyle;

 axisSet.yAxis.minorTickLength = 5.0f;

 axisSet.yAxis.majorTickLength = 7.0f;

 axisSet.yAxis.labelOffset = 3.0f;

 axisSet.yAxis.title = mGraphTitle;

 NSString *yAxisTitleLocation = [NSString stringWithFormat:@"%f", (yAxisMax/2)];

 axisSet.yAxis.titleLocation = [[NSDecimalNumber decimalNumberWithString:yAxisTitleLocation] decimalValue];


 //create bar plot

 CPBarPlot* barPlot = [[[CPBarPlot alloc] initWithFrame:graph.defaultPlotSpace.graph.bounds] autorelease];

 barPlot.identifier = @"mGraphTitle";

 barPlot.dataSource = self;


 //set bar width

 double spaceAvailableOnXaxis = IPHONE_SCREEN_WIDTH;

 //take out the padding on left and right

 spaceAvailableOnXaxis = spaceAvailableOnXaxis - (graph.paddingLeft + graph.paddingRight);

 //take out the small gap at the end of the xaxis.

 spaceAvailableOnXaxis = spaceAvailableOnXaxis*(xAxisMaxOriginal/xAxisMax);

 //the width of a bar is equal to half of the distance between successive marks

 //in other words, there will be twice as many bar slots as there are marks, and we use

 //every other bar slot

 double barWidthRatio = spaceAvailableOnXaxis/([mGraphDataArray count]*2);

 barPlot.barWidth = barWidthRatio;

 //drawing a bar should start from mark 1, not 0. Between 0 and one there are two bar slots so skip those.

 barPlot.barOffset = 2;


 //set bar fill

 CPFill* cpFill = [[CPFill alloc] initWithColor:[theme barFillColor]];

 barPlot.fill = cpFill;

 [cpFill release];


 //set bar label text size and color - not being used at the moment because there aren't any bar labels yet

 CPTextStyle* textStyle = [CPTextStyle textStyle];

 barPlot.barLabelTextStyle = textStyle;

 textStyle.fontSize = 20.0;


 //apply theme

 //axes label color

 axisSet.xAxis.labelTextStyle.color = [theme axesFontColor];

 axisSet.yAxis.labelTextStyle.color = [theme axesFontColor];

 axisSet.xAxis.titleTextStyle.color = [theme axesFontColor];

 axisSet.yAxis.titleTextStyle.color = [theme axesFontColor];

 //axes line color

 axisSet.xAxis.axisLineStyle.lineColor = [theme axesLineColor];

 axisSet.yAxis.axisLineStyle.lineColor = [theme axesLineColor];

 //axes tick mark color

 axisSet.xAxis.minorTickLineStyle.lineColor= [theme tickMarkColor];

 axisSet.xAxis.majorTickLineStyle.lineColor= [theme tickMarkColor];

 axisSet.yAxis.minorTickLineStyle.lineColor= [theme tickMarkColor];

 axisSet.yAxis.majorTickLineStyle.lineColor= [theme tickMarkColor];

 //bar plot border color

 //barPlot.borderColor = [theme barBorderColor];


 //set view background

 graphView.backgroundColor = [theme backgroundColor];


 //add graph to view

 [graph addPlot:barPlot];


 return [graphView autorelease];

}


- (void) renderGraphold {

 //create graph

 self.mGraph = [[CPXYGraph alloc] initWithFrame:self.mParentForGraphView.bounds];


//    self.mGraphView = [[CPLayerHostingView alloc]initWithFrame:[UIScreen mainScreen].bounds];

 self.mGraphView = [[CPLayerHostingView alloc]initWithFrame:self.mParentForGraphView.bounds];

 CPLayerHostingView *hostingView = (CPLayerHostingView *)self.mGraphView;

 hostingView.hostedLayer = self.mGraph;

 self.mGraph.paddingLeft = 55.0;

 self.mGraph.paddingTop = 20.0;

 self.mGraph.paddingRight = 15.0;

 self.mGraph.paddingBottom = 50.0;


 //set plot space

 double xAxisMaxOriginal = [self findMax:mGraphDataArray ForX:YES];

 double xAxisMax = [self findMax:mGraphDataArray ForX:YES];

 double yAxisMax = [self findMax:mGraphDataArray ForX:NO];

 //leave a small gap at the end so graph does go till the end

 xAxisMax = xAxisMax + 0.2*xAxisMax;

 yAxisMax = yAxisMax + 0.2*yAxisMax;


 CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)self.mGraph.defaultPlotSpace;

 plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0)

 length:CPDecimalFromFloat(xAxisMax)];

 plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0)

 length:CPDecimalFromFloat(yAxisMax)];


 //set line style

 CPLineStyle *lineStyle = [CPLineStyle lineStyle];

 lineStyle.lineColor = [CPColor blackColor];

 lineStyle.lineWidth = 2.0f;


 //setup x-axis


 CPXYAxisSet *axisSet = (CPXYAxisSet *)self.mGraph.axisSet;

 axisSet.xAxis.majorIntervalLength = [[NSDecimalNumber decimalNumberWithString:@"1"] decimalValue];

 axisSet.xAxis.minorTicksPerInterval = 0;

 axisSet.xAxis.majorTickLineStyle = lineStyle;

 axisSet.xAxis.minorTickLineStyle = lineStyle;

 axisSet.xAxis.axisLineStyle = lineStyle;

 axisSet.xAxis.minorTickLength = 5.0f;

 axisSet.xAxis.majorTickLength = 7.0f;

 axisSet.xAxis.labelOffset = 3.0f;

 axisSet.xAxis.title = X_AXIS_TITLE;

 NSString *xAxisTitleLocation = [NSString stringWithFormat:@"%f", (xAxisMax/2)];

 axisSet.xAxis.titleLocation = [[NSDecimalNumber decimalNumberWithString:xAxisTitleLocation] decimalValue];;

 NSNumberFormatter* formatter = [[[NSNumberFormatter alloc] init] autorelease];

 [formatter setNumberStyle:NSNumberFormatterDecimalStyle];

 axisSet.xAxis.labelFormatter = formatter;


 //setup y-axis

 //    axisSet.yAxis.majorIntervalLength = [[NSDecimalNumber decimalNumberWithString:@"5"] decimalValue];

 NSString *yAxisMajorIntervalLength = [NSString stringWithFormat:@"%f", (yAxisMax/5)];

 axisSet.yAxis.majorIntervalLength = [[NSDecimalNumber decimalNumberWithString:yAxisMajorIntervalLength] decimalValue];

 axisSet.yAxis.minorTicksPerInterval = 4;

 axisSet.yAxis.majorTickLineStyle = lineStyle;

 axisSet.yAxis.minorTickLineStyle = lineStyle;

 axisSet.yAxis.axisLineStyle = lineStyle;

 axisSet.yAxis.minorTickLength = 5.0f;

 axisSet.yAxis.majorTickLength = 7.0f;

 axisSet.yAxis.labelOffset = 3.0f;

 axisSet.yAxis.title = mGraphTitle;

 NSString *yAxisTitleLocation = [NSString stringWithFormat:@"%f", (yAxisMax/2)];

 axisSet.yAxis.titleLocation = [[NSDecimalNumber decimalNumberWithString:yAxisTitleLocation] decimalValue];


 /*

 //create line plot

 CPScatterPlot *linePlot = [[[CPScatterPlot alloc]

 initWithFrame:self.mGraph.defaultPlotSpace.graph.bounds] autorelease];

 linePlot.identifier = mGraphTitle;

 linePlot.dataLineStyle.lineWidth = 1.0f;

 linePlot.dataLineStyle.lineColor = [CPColor redColor];

 linePlot.dataSource = self;

 [self.mGraph addPlot:linePlot];


 //create markers

 CPPlotSymbol *greenCirclePlotSymbol = [CPPlotSymbol ellipsePlotSymbol];

 greenCirclePlotSymbol.fill = [CPFill fillWithColor:[CPColor greenColor]];

 greenCirclePlotSymbol.size = CGSizeMake(2.0, 2.0);

 [linePlot setPlotSymbol:greenCirclePlotSymbol];

 */


 //create bar plot

 CPBarPlot* barPlot = [[[CPBarPlot alloc] initWithFrame:self.mGraph.defaultPlotSpace.graph.bounds] autorelease];

 barPlot.identifier = @"mGraphTitle";

 barPlot.dataSource = self;


 //set bar width

 double spaceAvailableOnXaxis = IPHONE_SCREEN_WIDTH;

 //take out the padding on left and right

 spaceAvailableOnXaxis = spaceAvailableOnXaxis - (self.mGraph.paddingLeft + self.mGraph.paddingRight);

 //take out the small gap at the end of the xaxis.

 spaceAvailableOnXaxis = spaceAvailableOnXaxis*(xAxisMaxOriginal/xAxisMax);

 //the width of a bar is equal to half of the distance between successive marks

 //in other words, there will be twice as many bar slots as there are marks, and we use

 //every other bar slot

 double barWidthRatio = spaceAvailableOnXaxis/([mGraphDataArray count]*2);

 barPlot.barWidth = barWidthRatio;

 //drawing a bar should start from mark 1, not 0. Between 0 and one there are two bar slots so skip those.

 barPlot.barOffset = 2;


 //set bar fill

 CPFill* cpFill = [[CPFill alloc] initWithColor:[CPColor lightGrayColor]];

 barPlot.fill = cpFill;


 //set bar text

 CPTextStyle* textStyle = [CPTextStyle textStyle];

 barPlot.barLabelTextStyle = textStyle;

 textStyle.fontSize = 20.0;


 [self.mGraph addPlot:barPlot];

//    [self.view addSubview:self.mGraphView];

//    [self.mAnalysisInfo.options addSubview:mGraphView];

 [self.mParentForGraphView addSubview:mGraphView];

}


-(NSUInteger)numberOfRecordsForPlot:(CPPlot *)plot {

 return [mGraphDataArray count];

}


-(NSNumber *)numberForPlot:(CPPlot *)plot field:(NSUInteger)fieldEnum

 recordIndex:(NSUInteger)index

{

 NSLog(@"TaggedVideoGraphViewController.numberForPlot:field:recordIndex");

 GraphDataPoint* dataPoint = (GraphDataPoint*)[mGraphDataArray objectAtIndex:index];

 switch (fieldEnum) {

 case CPScatterPlotFieldX:

 return [NSNumber numberWithDouble:dataPoint.mX];

 break;

 case CPScatterPlotFieldY:

 return [NSNumber numberWithDouble:dataPoint.mY];

 break;

 default:

 NSLog(@"TaggedVideoGraphViewController.numberForPlot:field:recordIndex invalidd fieldEnum, %d", fieldEnum);

 return [NSNumber numberWithInteger:-1];

 break;

 }

}


- (NSMutableArray*) extractGraphArrayFromArray:(NSMutableArray*) array OfType:(GraphType)graphType {

 NSMutableArray* graphArray = [[NSMutableArray alloc] init];

 for (int i=0; i<[self.mAnalysisInfo.tagStatistics count]; i++) {

 GraphDataPoint* dataPoint = (GraphDataPoint*)[[GraphDataPoint alloc] init];

 TagStatisticsInformationBlock* infoBlock = (TagStatisticsInformationBlock*)[self.mAnalysisInfo.tagStatistics objectAtIndex:i];

 dataPoint.mX = infoBlock.mMark;

 switch (graphType) {

 case GRAPH_STROKE_COUNT:

 dataPoint.mY = infoBlock.mStrokeCount;

 break;

 case GRAPH_STROKE_FREQUENCY:

 dataPoint.mY = infoBlock.mStrokeFrequency;

 break;

 case GRAPH_TIME:

 dataPoint.mY = infoBlock.mTime;

 break;

 default:

 break;

 }

 [graphArray addObject:dataPoint];

 [dataPoint release];

 }

 return graphArray;

}


//if ForX==YES then max value for X will be searched, if not then max value for Y will be searched

- (double) findMax:(NSMutableArray*)array ForX:(Boolean)forX{

 double max = -1.0;

 for (int i=0; i<[array count]; i++) {

 GraphDataPoint* dataPoint = (GraphDataPoint*)[array objectAtIndex:i];

 if (forX == YES) {

 if (dataPoint.mX > max) {

 max = dataPoint.mX;

 }

 }else {

 if (dataPoint.mY > max) {

 max = dataPoint.mY;

 }

 }

 }

 return max;

}


- (IBAction) emailButtonPressed {

 MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];

 picker.mailComposeDelegate = self;


 // Email subject

 NSString* emailSubject = mGraphTitle;

 emailSubject = [emailSubject stringByAppendingString:@" Graph: "];

 emailSubject = [emailSubject stringByAppendingString:self.mAnalysisInfo.videoTag.TagDescription];

 emailSubject = [emailSubject stringByAppendingString:@" "];

 emailSubject = [emailSubject stringByAppendingString:[((EventInfo*)self.mAnalysisInfo.videoTag.EventInfo) EventDescriptionLong]];

 [picker setSubject:emailSubject];


 // Email recipients

 //NSArray *toRecipients = [NSArray arrayWithObject:@"tilaye@gmail.com"];

 //[picker setToRecipients:toRecipients];


 // Email Attachment

 /*

 UIGraphicsBeginImageContext(self.mGraphView.frame.size);

 // Do translations (image will be inverted otherwise)

 CGContextRef ctx = UIGraphicsGetCurrentContext();

 CGContextTranslateCTM(ctx, 0.0, self.mGraphView.frame.size.height);

 CGContextScaleCTM(ctx, 1.0, -1.0);

 [self.mGraphView.layer renderInContext:UIGraphicsGetCurrentContext()];

 UIImage* viewImage = UIGraphicsGetImageFromCurrentImageContext();

 UIGraphicsEndImageContext();

 */


 id<GraphTheme> theme = [[GraphThemeLight alloc] init];

 CPLayerHostingView* graphView = [self createGraphWithTheme:theme];

 UIGraphicsBeginImageContext(graphView.frame.size);

 // Do translations (image will be inverted otherwise)

 CGContextRef ctx = UIGraphicsGetCurrentContext();

 CGContextTranslateCTM(ctx, 0.0, graphView.frame.size.height);

 CGContextScaleCTM(ctx, 1.0, -1.0);

 [graphView.layer renderInContext:UIGraphicsGetCurrentContext()];

 UIImage* viewImage = UIGraphicsGetImageFromCurrentImageContext();

 UIGraphicsEndImageContext();


 NSData* myData = UIImagePNGRepresentation(viewImage);

 [picker addAttachmentData:myData mimeType:@"image/png" fileName:@"Filename"];


 // Email text

 // name

 NSString *emailBody = @"Name: ";

 emailBody = [emailBody stringByAppendingString:self.mAnalysisInfo.videoTag.TagDescription];

 emailBody = [emailBody stringByAppendingString:@"\n"];

 //style

 emailBody = [emailBody stringByAppendingString:@"Style: "];

 emailBody = [emailBody stringByAppendingString:[((EventInfo*)self.mAnalysisInfo.videoTag.EventInfo) EventDescriptionLong]];

 emailBody = [emailBody stringByAppendingString:@"\n"];

 //time

 NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];

 [dateFormatter setDateFormat:@"dd MMMMM yyyy hh:mm:ss aaa"];

 emailBody = [emailBody stringByAppendingString:@"Time: "];

 NSString* formattedDate = [dateFormatter stringFromDate:(NSDate*)self.mAnalysisInfo.videoTag.TagDate];

 emailBody = [emailBody stringByAppendingString:formattedDate];

 [picker setMessageBody:emailBody isHTML:NO];

 [dateFormatter release];


 [self.mAnalysisInfo.parentView.navigationController  presentModalViewController:picker animated:YES];

 [picker release];


}


// Dismisses the email composition interface when users tap Cancel or Send. Proceeds to update the message field with the result of the operation.

- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error

{

 NSString* emailStatus;

 // Notifies users about errors associated with the interface

 switch (result)

 {

 case MFMailComposeResultCancelled:

 emailStatus = @"Result: canceled";

 break;

 case MFMailComposeResultSaved:

 emailStatus = @"Result: saved";

 break;

 case MFMailComposeResultSent:

 emailStatus = @"Result: sent";

 break;

 case MFMailComposeResultFailed:

 emailStatus = @"Result: failed";

 break;

 default:

 emailStatus = @"Result: not sent";

 break;

 }

 [self.mAnalysisInfo.parentView.navigationController dismissModalViewControllerAnimated:YES];

}


- (BOOL)textFieldShouldReturn:(UITextField *)textField {

 [textField resignFirstResponder];

 return YES;

}


- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text

{

 NSLog(@"TaggedVideoGraphViewController.textView:shouldChangeTextInRange:replacementText:");

 // Any new character added is passed in as the "text" parameter

 if (1) {

 // Be sure to test for equality using the "isEqualToString" message

 [textView resignFirstResponder];


 // Return FALSE so that the final '\n' character doesn't get added

 return FALSE;

 }

 // For any other character return TRUE so that the text gets added to the view

 return TRUE;

}


- (void) viewWillDisappear:(BOOL)animated {

 [super viewWillDisappear:animated];

 [mGraphView removeFromSuperview];

}


- (void)dealloc {

 [mGraph release];

 [mGraphDataArray release];

 [mGraphTitle release];

 [mEmailButton release];

 [mAnalysisInfo release];

 [mGraphView release];

 [mParentForGraphView release];

 [mLabelBackgroundText release];

 [super dealloc];

}


@end

<pre>

 

Update 2 Theme files

//
//  GraphThemeDark.h
//  SwimmerApp
//
//  Created by tilaye on 2/28/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "GraphTheme.h"

@interface GraphThemeDark : NSObject <GraphTheme> {

}

@end

//
//  GraphThemeDark.m
//  SwimmerApp
//
//  Created by tilaye on 2/28/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "GraphThemeDark.h"


@implementation GraphThemeDark

- (UIColor*) backgroundColor {
 return [UIColor blackColor];
}

- (CPColor*) axesFontColor {
 return [CPColor whiteColor];
}

- (CPColor*) barFillColor {
 return [CPColor darkGrayColor];
}

- (CPColor*) axesLineColor {
 return [CPColor darkGrayColor];
}

- (CPColor*) tickMarkColor {
 return [CPColor darkGrayColor];
}

- (CPColor*) barBorderColor {
 return [CPColor darkGrayColor];
}

@end

//
//  GraphThemeLight.h
//  SwimmerApp
//
//  Created by tilaye on 2/28/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "GraphTheme.h"

@interface GraphThemeLight : NSObject <GraphTheme> {

}

@end

//
//  GraphThemeLight.m
//  SwimmerApp
//
//  Created by tilaye on 2/28/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "GraphThemeLight.h"
#import "GraphTheme.h"


@implementation GraphThemeLight

- (UIColor*) backgroundColor {
 return [UIColor whiteColor];
}

- (CPColor*) axesFontColor {
 return [CPColor blackColor];
}

- (CPColor*) barFillColor {
 return [CPColor lightGrayColor];
}

- (CPColor*) axesLineColor {
 return [CPColor blackColor];
}

- (CPColor*) tickMarkColor {
 return [CPColor blackColor];
}

- (CPColor*) barBorderColor {
 return [CPColor blackColor];
}

@end



<pre>

26 thoughts on “Bar/histogram on core-plot

  1. Hello,
    Thank you for this little example… I try to make the same kind of graph, but I don’t succeed: my bars are superimposed. Could it be possible for you to share the whole code of your graph? Or to send it to me by mail… Thank you. :)

  2. Hi,
    Thanks for the tutorial!!!!
    I’ve been struggling for a long while on installing coreplot framework but I finally succeed on not getting any error when inserting the following sentence into my code #import “CorePlot-CocoaTouch.h”
    First step done ;-)
    Unfortunately, I’ m now stuck without any clear code to follow at start.
    Would you mind sharing your code for this graph?

    Regards,
    Patrick

  3. Hi Patrick, no problem. I’ll email it to you shortly.

    It seems I need to find the time and post the full code here!

  4. Hi,

    I am not knowing how to get the small ticks on the x-axis for bar graph. Can u help me out?

  5. Hi,
    nice work here, I’m trying to use core-plot into my app like you did but I don’t have start point and I have lot of errors.
    Is it possible to upload your code example so that I could take a look to a working project?
    Thank you

  6. Thank you for this tutorial!! One question though, could you please give an example of your data? I can see its an array but I can’t find any more info.

  7. Thank your for posting your code. Do you a link for download? Could it be possible for you to share the whole code of your graph?

    Thanks again!

  8. Hi, Very nice tutorial. Can any one please guide me how to remove Bar border in Bar chart created above

  9. 1)i need the source code where you are increasing width of the bars.
    2) Also i need to make grouped bar graphs with each group having 2 bars.
    3) I need to know how do we get data to pass to bar graphs.
    Your help will be useful.

  10. Hi blogger, i must say you have very interesting articles here.

    Your website can go viral. You need initial traffic only.
    How to get it? Search for; Mertiso’s tips go viral

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>