Joerg Polakowski

updated PNLineChart to allow the display of multiple lines in the chart

... ... @@ -30,6 +30,8 @@
9F55483E18498E0E004073B5 /* PNCircleChart.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F55483D18498E0E004073B5 /* PNCircleChart.m */; };
9F656B51184A4E34002E5675 /* UICountingLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F656B50184A4E34002E5675 /* UICountingLabel.m */; };
9FA23B10184A5944002DBBA4 /* PCChartsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FA23B0F184A5944002DBBA4 /* PCChartsTableViewController.m */; };
AF3EA32ACA7D5DAC2167CABD /* PNLineChartData.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3EAE8875DCB1D0BDF690E0 /* PNLineChartData.m */; };
AF3EA5ABA289F1726E048CF0 /* PNLineChartDataItem.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3EA2A586F5DB3F10DEF9C0 /* PNLineChartDataItem.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
... ... @@ -81,6 +83,10 @@
9FA23B0E184A5944002DBBA4 /* PCChartsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCChartsTableViewController.h; sourceTree = "<group>"; };
9FA23B0F184A5944002DBBA4 /* PCChartsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PCChartsTableViewController.m; sourceTree = "<group>"; };
9FE9CFB818581102005B8223 /* PNChartDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PNChartDelegate.h; path = PNChart/PNChartDelegate.h; sourceTree = "<group>"; };
AF3EA2A586F5DB3F10DEF9C0 /* PNLineChartDataItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNLineChartDataItem.m; sourceTree = "<group>"; };
AF3EA71805621F984344CF8D /* PNLineChartData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNLineChartData.h; sourceTree = "<group>"; };
AF3EA9564304AC28E21019E4 /* PNLineChartDataItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNLineChartDataItem.h; sourceTree = "<group>"; };
AF3EAE8875DCB1D0BDF690E0 /* PNLineChartData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNLineChartData.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
... ... @@ -222,6 +228,10 @@
children = (
0AF7A8B1182AC604003645C4 /* PNLineChart.h */,
0AF7A8B2182AC604003645C4 /* PNLineChart.m */,
AF3EA2A586F5DB3F10DEF9C0 /* PNLineChartDataItem.m */,
AF3EAE8875DCB1D0BDF690E0 /* PNLineChartData.m */,
AF3EA71805621F984344CF8D /* PNLineChartData.h */,
AF3EA9564304AC28E21019E4 /* PNLineChartDataItem.h */,
);
name = PNLineChart;
sourceTree = "<group>";
... ... @@ -357,6 +367,8 @@
0AF7A8B3182AC604003645C4 /* PNLineChart.m in Sources */,
9F55483E18498E0E004073B5 /* PNCircleChart.m in Sources */,
0AF7A878182AA9F6003645C4 /* PCAppDelegate.m in Sources */,
AF3EA5ABA289F1726E048CF0 /* PNLineChartDataItem.m in Sources */,
AF3EA32ACA7D5DAC2167CABD /* PNLineChartData.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ...
... ... @@ -8,6 +8,8 @@
#import "PCChartsTableViewController.h"
#import "PNChart.h"
#import "PNLineChartData.h"
#import "PNLineChartDataItem.h"
@interface PCChartsTableViewController ()
... ... @@ -61,7 +63,26 @@
PNLineChart * lineChart = [[PNLineChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 200.0)];
lineChart.backgroundColor = [UIColor clearColor];
[lineChart setXLabels:@[@"SEP 1",@"SEP 2",@"SEP 3",@"SEP 4",@"SEP 5",@"SEP 6",@"SEP 7"]];
[lineChart setYValues:@[@1,@24,@12,@18,@30,@10,@21]];
// Line Chart Nr.1
PNLineChartData *data01 = [PNLineChartData new];
data01.color = [UIColor redColor];
data01.itemCount = lineChart.xLabels.count;
data01.getData = ^(NSUInteger item) {
CGFloat y = item * 10;
return [PNLineChartDataItem dataItemWithY:y];
};
// Line Chart Nr.2
PNLineChartData *data02 = [PNLineChartData new];
data02.color = [UIColor blueColor];
data02.itemCount = lineChart.xLabels.count;
data02.getData = ^(NSUInteger item) {
CGFloat y = item == 0 ? (item * 5) + 10 : (item * 5);
return [PNLineChartDataItem dataItemWithY:y];
};
lineChart.chartData = @[data01, data02];
[lineChart strokeChart];
lineChart.delegate = self;
... ...
... ... @@ -29,7 +29,10 @@
@property (strong, nonatomic) NSArray * yLabels;
@property (strong, nonatomic) NSArray * yValues;
/**
* Array of `LineChartData` objects, one for each line.
*/
@property (strong, nonatomic) NSArray *chartData;
@property (strong, nonatomic) NSMutableArray * pathPoints;
... ... @@ -41,10 +44,6 @@
@property (nonatomic) float xLabelHeight;
@property (nonatomic,strong) CAShapeLayer * chartLine;
@property (nonatomic, strong) UIColor * strokeColor;
@property (nonatomic) BOOL showLabel;
@property (nonatomic, strong) UIBezierPath *progressline;
... ...
... ... @@ -9,61 +9,50 @@
#import "PNLineChart.h"
#import "PNColor.h"
#import "PNChartLabel.h"
#import "PNLineChartData.h"
#import "PNLineChartDataItem.h"
//------------------------------------------------------------------------------------------------
// private interface declaration
//------------------------------------------------------------------------------------------------
@interface PNLineChart ()
@property (nonatomic,strong) NSMutableArray *chartLineArray; // Array[CAShapeLayer]
- (void)setDefaultValues;
@end
//------------------------------------------------------------------------------------------------
// public interface implementation
//------------------------------------------------------------------------------------------------
@implementation PNLineChart
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
#pragma mark initialization
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
// Initialization code
self.backgroundColor = [UIColor whiteColor];
self.clipsToBounds = YES;
_chartLine = [CAShapeLayer layer];
_chartLine.lineCap = kCALineCapRound;
_chartLine.lineJoin = kCALineJoinBevel;
_chartLine.fillColor = [[UIColor whiteColor] CGColor];
_chartLine.lineWidth = 3.0;
_chartLine.strokeEnd = 0.0;
_showLabel = YES;
_pathPoints = [[NSMutableArray alloc] init];
self.userInteractionEnabled = YES;
_xLabelHeight = 20.0;
_chartCavanHeight = self.frame.size.height - chartMargin * 2 - _xLabelHeight*2 ;
[self.layer addSublayer:_chartLine];
[self setDefaultValues];
}
return self;
}
-(void)setYValues:(NSArray *)yValues
{
_yValues = yValues;
float max = 0;
for (NSString * valueString in yValues) {
float value = [valueString floatValue];
if (value > max) {
max = value;
}
}
//Min value for Y label
if (max < 5) {
max = 5;
}
_yValueMax = (float)max;
if (_showLabel) {
[self setYLabels:yValues];
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setDefaultValues];
}
return self;
}
#pragma mark instance methods
-(void)setYLabels:(NSArray *)yLabels
{
float level = _yValueMax / 5.0;
NSInteger index = 0;
... ... @@ -99,12 +88,6 @@
}
-(void)setStrokeColor:(UIColor *)strokeColor
{
_strokeColor = strokeColor;
_chartLine.strokeColor = [strokeColor CGColor];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self chechPoint:touches withEvent:event];
... ... @@ -136,65 +119,132 @@
-(void)strokeChart
{
UIGraphicsBeginImageContext(self.frame.size);
_progressline = [UIBezierPath bezierPath];
CGFloat firstValue = [[_yValues objectAtIndex:0] floatValue];
CGFloat xPosition = _xLabelWidth;
if(!_showLabel){
_chartCavanHeight = self.frame.size.height - _xLabelHeight*2;
xPosition = 0;
}
float grade = (float)firstValue / (float)_yValueMax;
for (NSUInteger a = 0; a < self.chartData.count; a++) {
PNLineChartData *chartData = self.chartData[a];
CAShapeLayer *chartLine = (CAShapeLayer *) self.chartLineArray[a];
[_progressline moveToPoint:CGPointMake( xPosition, _chartCavanHeight - grade * _chartCavanHeight + _xLabelHeight)];
[_pathPoints addObject:[NSValue valueWithCGPoint:CGPointMake( xPosition, _chartCavanHeight - grade * _chartCavanHeight + _xLabelHeight)]];
[_progressline setLineWidth:3.0];
[_progressline setLineCapStyle:kCGLineCapRound];
[_progressline setLineJoinStyle:kCGLineJoinRound];
NSInteger index = 0;
for (NSString * valueString in _yValues) {
float value = [valueString floatValue];
float grade = (float)value / (float)_yValueMax;
if (index != 0) {
CGPoint point = CGPointMake(index * _xLabelWidth + 30.0 + _xLabelWidth /2.0, _chartCavanHeight - (grade * _chartCavanHeight) + _xLabelHeight);
[_pathPoints addObject:[NSValue valueWithCGPoint:point]];
[_progressline addLineToPoint:point];
[_progressline moveToPoint:point];
UIGraphicsBeginImageContext(self.frame.size);
_progressline = [UIBezierPath bezierPath];
PNLineChartDataItem *firstDataItem = chartData.getData(0);
CGFloat firstValue = firstDataItem.y;
CGFloat xPosition = _xLabelWidth;
if(!_showLabel){
_chartCavanHeight = self.frame.size.height - _xLabelHeight*2;
xPosition = 0;
}
CGFloat grade = (float)firstValue / _yValueMax;
[_progressline moveToPoint:CGPointMake( xPosition, _chartCavanHeight - grade * _chartCavanHeight + _xLabelHeight)];
[_pathPoints addObject:[NSValue valueWithCGPoint:CGPointMake( xPosition, _chartCavanHeight - grade * _chartCavanHeight + _xLabelHeight)]];
[_progressline setLineWidth:3.0];
[_progressline setLineCapStyle:kCGLineCapRound];
[_progressline setLineJoinStyle:kCGLineJoinRound];
NSInteger index = 0;
for (NSUInteger i = 0; i < chartData.itemCount; i++) {
PNLineChartDataItem *dataItem = chartData.getData(i);
float value = dataItem.y;
CGFloat innerGrade = value / _yValueMax;
if (index != 0) {
CGPoint point = CGPointMake(index * _xLabelWidth + 30.0 + _xLabelWidth / 2.0, _chartCavanHeight - (innerGrade * _chartCavanHeight) + _xLabelHeight);
[_pathPoints addObject:[NSValue valueWithCGPoint:point]];
[_progressline addLineToPoint:point];
[_progressline moveToPoint:point];
}
index += 1;
}
index += 1;
// setup the color of the chart line
if (chartData.color) {
chartLine.strokeColor = [chartData.color CGColor];
}else{
chartLine.strokeColor = [PNGreen CGColor];
}
[_progressline stroke];
chartLine.path = _progressline.CGPath;
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = 1.0;
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[chartLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
chartLine.strokeEnd = 1.0;
UIGraphicsEndImageContext();
}
[_progressline stroke];
_chartLine.path = _progressline.CGPath;
if (_strokeColor) {
_chartLine.strokeColor = [_strokeColor CGColor];
}else{
_chartLine.strokeColor = [PNGreen CGColor];
}
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
pathAnimation.duration = 1.0;
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[_chartLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
_chartLine.strokeEnd = 1.0;
UIGraphicsEndImageContext();
}
- (void)setChartData:(NSArray *)data {
if (data != _chartData) {
NSMutableArray *yLabelsArray = [NSMutableArray arrayWithCapacity:data.count];
CGFloat yMax = 0.0f;
// remove all shape layers before adding new ones
for (CALayer *layer in self.chartLineArray) {
[layer removeFromSuperlayer];
}
self.chartLineArray = [NSMutableArray arrayWithCapacity:data.count];
for (PNLineChartData *chartData in data) {
// create as many chart line layers as there are data-lines
CAShapeLayer *chartLine = [CAShapeLayer layer];
chartLine.lineCap = kCALineCapRound;
chartLine.lineJoin = kCALineJoinBevel;
chartLine.fillColor = [[UIColor whiteColor] CGColor];
chartLine.lineWidth = 3.0;
chartLine.strokeEnd = 0.0;
[self.layer addSublayer:chartLine];
[self.chartLineArray addObject:chartLine];
for (NSUInteger i = 0; i < chartData.itemCount; i++) {
PNLineChartDataItem *dataItem = chartData.getData(i);
CGFloat yValue = dataItem.y;
[yLabelsArray addObject:[NSString stringWithFormat:@"%2f", yValue]];
yMax = fmaxf(yMax, dataItem.y);
}
}
// Min value for Y label
if (yMax < 5) {
yMax = 5.0f;
}
_yValueMax = yMax;
_chartData = data;
if (_showLabel) {
[self setYLabels:yLabelsArray];
}
[self setNeedsDisplay];
}
}
#pragma mark private methods
- (void)setDefaultValues {
// Initialization code
self.backgroundColor = [UIColor whiteColor];
self.clipsToBounds = YES;
self.chartLineArray = [NSMutableArray new];
_showLabel = YES;
_pathPoints = [[NSMutableArray alloc] init];
self.userInteractionEnabled = YES;
_xLabelHeight = 20.0;
_chartCavanHeight = self.frame.size.height - chartMargin * 2 - _xLabelHeight*2 ;
}
@end
... ...
//
// Created by Jörg Polakowski on 14/12/13.
// Copyright (c) 2013 kevinzhow. All rights reserved.
//
#import <Foundation/Foundation.h>
@class PNLineChartDataItem;
typedef PNLineChartDataItem *(^LCLineChartDataGetter)(NSUInteger item);
/**
*
*/
@interface PNLineChartData : NSObject
@property (strong) UIColor *color;
@property NSUInteger itemCount;
@property (copy) LCLineChartDataGetter getData;
@end
... ...
//
// Created by Jörg Polakowski on 14/12/13.
// Copyright (c) 2013 kevinzhow. All rights reserved.
//
#import "PNLineChartData.h"
@implementation PNLineChartData
@end
... ...
//
// Created by Jörg Polakowski on 14/12/13.
// Copyright (c) 2013 kevinzhow. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface PNLineChartDataItem : NSObject
@property (readonly) CGFloat y; // should be within the y range
+ (PNLineChartDataItem *)dataItemWithY:(CGFloat)y;
@end
... ...
//
// Created by Jörg Polakowski on 14/12/13.
// Copyright (c) 2013 kevinzhow. All rights reserved.
//
#import "PNLineChartDataItem.h"
//------------------------------------------------------------------------------------------------
// private interface declaration
//------------------------------------------------------------------------------------------------
@interface PNLineChartDataItem ()
@property (readwrite) CGFloat y; // should be within the y range
- (id)initWithY:(CGFloat)y;
@end
//------------------------------------------------------------------------------------------------
// public interface implementation
//------------------------------------------------------------------------------------------------
@implementation PNLineChartDataItem
+ (PNLineChartDataItem *)dataItemWithY:(CGFloat)y {
return [[PNLineChartDataItem alloc] initWithY:y];
}
- (id)initWithY:(CGFloat)y {
if ((self = [super init])) {
self.y = y;
}
return self;
}
@end
... ...
... ... @@ -40,7 +40,26 @@ You will need LLVM 3.0 or later in order to build PNChart.
//For LineChart
PNLineChart * lineChart = [[PNLineChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 200.0)];
[lineChart setXLabels:@[@"SEP 1",@"SEP 2",@"SEP 3",@"SEP 4",@"SEP 5"]];
[lineChart setYValues:@[@1, @10, @2, @6, @3]];
// Line Chart Nr.1
PNLineChartData *data01 = [PNLineChartData new];
data01.color = [UIColor redColor];
data01.itemCount = lineChart.xLabels.count;
data01.getData = ^(NSUInteger item) {
CGFloat y = item * 10;
return [PNLineChartDataItem dataItemWithY:y];
};
// Line Chart Nr.2
PNLineChartData *data02 = [PNLineChartData new];
data02.color = [UIColor blueColor];
data02.itemCount = lineChart.xLabels.count;
data02.getData = ^(NSUInteger item) {
CGFloat y = item == 0 ? (item * 5) + 10 : (item * 5);
return [PNLineChartDataItem dataItemWithY:y];
};
lineChart.chartData = @[data01, data02];
[lineChart strokeChart];
```
... ...