I'm trying to create a class that I can use to create bar, pie, and line graphs using the Core Plot library for an iOS application. I want to create some standardization in this class so that I only have to change a few options for each view controller that inherits my graph class. I have the pie graphs working the way I want, but I'm having issues with the bar graphs. I want a bar graph that is horizontal and where the Y axis is fixed for each plot based on how many plots there are.
Code:
enum
{
PieGraph = 0,
LineGraph,
BarGraph
};
Graph.h:
#import "CorePlot-CocoaTouch.h"
#import "BaseViewController.h"
CPTGraphHostingView *hostView;
CPTTheme *selectedTheme;
/*************** graphing options *******************/
int graphType;
int fillPercentage;
CGFloat xMax;
CGFloat yMax;
NSString *graphTitle;
NSString *xAxisTitle;
NSString *yAxisTitle;
BOOL graphOnBottom;
@interface Graph : BaseViewController<CPTPlotDataSource, UIActionSheetDelegate, CPTBarPlotDataSource, CPTBarPlotDelegate>
/*************** Methods *****************/
// graphing methods
-(void)initPlot;
-(void)configureHost;
-(void)configureGraph;
-(void)configureChart;
-(void)configureLegend;
-(void)configurePlots;
-(void)configureAxes;
@end
Graph.m:
#import "Graph.h"
@implementation Graph
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self initPlot];
}
/************************ Methods *************************/
// initialize the entire graph plot
-(void)initPlot
{
[self configureHost];
[self configureGraph];
if (graphType == PieGraph)
{
[self configureChart];
[self configureLegend];
}
else if (graphType == BarGraph)
{
[self configurePlots];
[self configureAxes];
}
}
// configure the host
-(void)configureHost
{
CGRect parentRect = self.view.bounds;
int maxHeight = (parentRect.size.height - 55);
int height = (maxHeight - ((100 - fillPercentage) * maxHeight / 100));
int yPosition;
if (graphOnBottom) yPosition = (maxHeight - height + 55);
else yPosition = 55;
parentRect = CGRectMake(parentRect.origin.x, yPosition, parentRect.size.width, height);
hostView = [(CPTGraphHostingView *) [CPTGraphHostingView alloc] initWithFrame:parentRect];
hostView.allowPinchScaling = NO;
[self.view addSubview:hostView];
}
// configure the graph
-(void)configureGraph
{
CPTGraph *graph = [[CPTXYGraph alloc] initWithFrame:hostView.bounds];
hostView.hostedGraph = graph;
if (graphType == PieGraph)
{
graph.paddingLeft = 0.0f;
graph.paddingTop = 0.0f;
graph.paddingRight = 0.0f;
graph.paddingBottom = 0.0f;
graph.axisSet = nil;
}
else if (graphType == BarGraph)
{
graph.plotAreaFrame.masksToBorder = NO;
if ([xAxisTitle isEqualToString:@""]) graph.paddingBottom = 0.0f;
else graph.paddingBottom = 30.0f;
if ([yAxisTitle isEqualToString:@""]) graph.paddingLeft = 0.0f;
else graph.paddingLeft = 30.0f;
graph.paddingTop = 0.0f;
graph.paddingRight = 0.0f;
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *) graph.defaultPlotSpace;
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromCGFloat(0.0f) length:CPTDecimalFromCGFloat(xMax)];
plotSpace.yRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromCGFloat(0.0f) length:CPTDecimalFromCGFloat(yMax)];
}
else if (graphType == LineGraph)
{
}
CPTMutableTextStyle *textStyle = [CPTMutableTextStyle textStyle];
textStyle.color = [CPTColor blackColor];
textStyle.fontName = @"Helvetica-Bold";
textStyle.fontSize = 12.0f;
graph.title = graphTitle;
graph.titleTextStyle = textStyle;
graph.titlePlotAreaFrameAnchor = CPTRectAnchorTop;
selectedTheme = [CPTTheme themeNamed:kCPTPlainWhiteTheme];
[graph applyTheme:selectedTheme];
}
// configure the chart
-(void)configureChart
{
CPTGraph *graph = hostView.hostedGraph;
CPTGradient *overlayGradient = [[CPTGradient alloc] init];
overlayGradient.gradientType = CPTGradientTypeRadial;
overlayGradient = [overlayGradient addColorStop:[[CPTColor blackColor] colorWithAlphaComponent:0.0] atPosition:0.9];
overlayGradient = [overlayGradient addColorStop:[[CPTColor blackColor] colorWithAlphaComponent:0.4] atPosition:1.0];
CPTPieChart *pieChart = [[CPTPieChart alloc] init];
pieChart.dataSource = self;
pieChart.delegate = self;
pieChart.pieRadius = (hostView.bounds.size.height * 0.7) / 2.5;
pieChart.identifier = graph.title;
pieChart.startAngle = M_PI_4;
pieChart.sliceDirection = CPTPieDirectionClockwise;
pieChart.overlayFill = [CPTFill fillWithGradient:overlayGradient];
[graph addPlot:pieChart];
}
// configure plots
-(void)configurePlots
{
CPTGraph *graph = hostView.hostedGraph;
if (graphType == BarGraph)
{
CPTMutableLineStyle *barLineStyle = [[CPTMutableLineStyle alloc] init];
barLineStyle.lineColor = [CPTColor lightGrayColor];
barLineStyle.lineWidth = 0.5;
CPTBarPlot *bar = [CPTBarPlot tubularBarPlotWithColor:[CPTColor redColor] horizontalBars:YES];
bar.dataSource = self;
bar.delegate = self;
bar.barWidth = CPTDecimalFromFloat(2.5f);
bar.barOffset = CPTDecimalFromFloat(2.5f);
bar.identifier = @"redBar";
bar.lineStyle = barLineStyle;
CPTBarPlot *bar2 = [CPTBarPlot tubularBarPlotWithColor:[CPTColor greenColor] horizontalBars:YES];
bar2.dataSource = self;
bar2.delegate = self;
bar2.barWidth = CPTDecimalFromFloat(2.5f);
bar2.barOffset = CPTDecimalFromFloat(2.5f);
bar2.identifier = @"greenBar";
bar2.lineStyle = barLineStyle;
[graph addPlot:bar toPlotSpace:graph.defaultPlotSpace];
[graph addPlot:bar2 toPlotSpace:graph.defaultPlotSpace];
}
else if (graphType == LineGraph)
{
}
}
// configure axes
-(void)configureAxes
{
if (graphType == BarGraph)
{
CPTMutableTextStyle *axisTitleStyle = [CPTMutableTextStyle textStyle];
axisTitleStyle.color = [CPTColor blackColor];
axisTitleStyle.fontName = @"Helvetica-Bold";
axisTitleStyle.fontSize = 12.0f;
CPTMutableLineStyle *axisLineStyle = [CPTMutableLineStyle lineStyle];
axisLineStyle.lineWidth = 2.0f;
axisLineStyle.lineColor = [[CPTColor blackColor] colorWithAlphaComponent:1];
CPTXYAxisSet *axisSet = (CPTXYAxisSet *) hostView.hostedGraph.axisSet;
axisSet.xAxis.labelingPolicy = CPTAxisLabelingPolicyNone;
axisSet.xAxis.title = xAxisTitle;
axisSet.xAxis.titleTextStyle = axisTitleStyle;
axisSet.xAxis.titleOffset = 5.0f;
axisSet.xAxis.axisLineStyle = axisLineStyle;
axisSet.yAxis.labelingPolicy = CPTAxisLabelingPolicyNone;
axisSet.yAxis.title = yAxisTitle;
axisSet.yAxis.titleTextStyle = axisTitleStyle;
axisSet.yAxis.titleOffset = 5.0f;
axisSet.yAxis.axisLineStyle = axisLineStyle;
}
else if (graphType == LineGraph)
{
}
}
// configure the legend
-(void)configureLegend
{
CPTGraph *graph = hostView.hostedGraph;
CPTLegend *legend = [CPTLegend legendWithGraph:graph];
legend.numberOfColumns = 1;
legend.fill = [CPTFill fillWithColor:[CPTColor whiteColor]];
legend.borderLineStyle = [CPTLineStyle lineStyle];
legend.cornerRadius = 5.0;
graph.legend = legend;
graph.legendAnchor = CPTRectAnchorBottomLeft;
//CGFloat legendPadding = -(self.view.bounds.size.width / 8);
graph.legendDisplacement = CGPointMake(3.0, 3.0);
}
@end
And finally the view controller class I am testing all of this on:
TestView.h:
#import "Graph.h"
NSArray *channel;
int offline;
int online;
@interface TestView : Graph
/*************** Methods *****************/
// back button
- (IBAction)backButtonClicked:(id)sender;
// handle view load
-(void) handleViewLoad;
// set arrays
-(void)setArrays;
@end
TestView.m:
#import "TestView.h"
@interface TestView ()
@end
@implementation TestView
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self handleViewLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)backButtonClicked:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
/*************** graph things ****************/
-(NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot
{
return 1;
}
-(NSNumber*)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)idx
{
int returnVal = 0;
NSLog([NSString stringWithFormat:@"idx: %d", idx]);
/*switch (idx)
{
case 0:
for (int x = 0; x < [channel count]; x++)
{
if ([channel[x] integerValue] == 20) returnVal ++;
}
offline = returnVal;
return [NSNumber numberWithInt:returnVal];
break;
case 1:
for (int x = 0; x < [channel count]; x++)
{
if ([channel[x] integerValue] != 20) returnVal ++;
}
online = returnVal;
return [NSNumber numberWithInt:returnVal];
break;
}*/
if ([plot.identifier isEqual:@"greenBar"])
{
for (int x = 0; x < [channel count]; x++)
{
if ([channel[x] integerValue] != 20) returnVal ++;
}
online = returnVal;
return [NSNumber numberWithInt:returnVal];
}
else if ([plot.identifier isEqual:@"redBar"])
{
for (int x = 0; x < [channel count]; x++)
{
if ([channel[x] integerValue] == 20) returnVal ++;
}
offline = returnVal;
return [NSNumber numberWithInt:returnVal];
}
return 0;
}
-(CPTLayer*)dataLabelForPlot:(CPTPlot *)plot recordIndex:(NSUInteger)idx
{
/*switch (idx)
{
case 0:
return [[CPTTextLayer alloc] initWithText:[NSString stringWithFormat:@"Offline: %d", offline]];
break;
default:
return [[CPTTextLayer alloc] initWithText:[NSString stringWithFormat:@"Online: %d", online]];
break;
}*/
if ([plot.identifier isEqual:@"greenBar"])
{
return [[CPTTextLayer alloc] initWithText:[NSString stringWithFormat:@"Online: %d", online]];
}
else if ([plot.identifier isEqual:@"redBar"])
{
return [[CPTTextLayer alloc] initWithText:[NSString stringWithFormat:@"Offline: %d", offline]];
}
return nil;
}
-(NSString*)legendTitleForPieChart:(CPTPieChart*)pieChart recordIndex:(NSUInteger)idx
{
switch (idx)
{
case 0:
return @"Offline";
break;
default:
return @"Online";
break;
}
return @"";
}
// handle view load
-(void)handleViewLoad
{
session = [BaseViewController getSession];
[self setArrays];
graphType = BarGraph;
fillPercentage = 50;
graphOnBottom = NO;
graphTitle = @"Coordinators Online/Offline";
xAxisTitle = @"Number of Devices";
yAxisTitle = @"";
xMax = (CGFloat)([channel count] + ([channel count] * 0.30f));
yMax = (CGFloat)([channel count] + ([channel count] * 0.30f));
[self initPlot];
}
// set arrays
-(void)setArrays
{
channel = [Json extractCoordinatorChannel:session];
}
@end
Here are some examples of what my graphs look like:
As you can see, the issue is that both the x and y axes are changing based on the value. I need only the x axis to change, and the y axis to be fixed for each plot.