MrWooJ

Adding Scatter methods

PNScatterChart in UIView
  1 +//
  2 +// PNScatterChart.h
  3 +// PNChartDemo
  4 +//
  5 +// Created by Alireza Arabi on 12/4/14.
  6 +// Copyright (c) 2014 kevinzhow. All rights reserved.
  7 +//
  8 +
  9 +#import <UIKit/UIKit.h>
  10 +#import <QuartzCore/QuartzCore.h>
  11 +#import "PNChartDelegate.h"
  12 +#import "PNScatterChartData.h"
  13 +#import "PNScatterChartDataItem.h"
  14 +
  15 +@interface PNScatterChart : UIView
  16 +
  17 +@property (nonatomic, retain) id<PNChartDelegate> delegate;
  18 +
  19 +/** Array of `ScatterChartData` objects, one for each line. */
  20 +@property (nonatomic) NSArray *chartData;
  21 +
  22 +/** Controls whether to show the coordinate axis. Default is NO. */
  23 +@property (nonatomic, getter = isShowCoordinateAxis) BOOL showCoordinateAxis;
  24 +@property (nonatomic) UIColor *axisColor;
  25 +@property (nonatomic) CGFloat axisWidth;
  26 +
  27 +/** String formatter for float values in y-axis labels. If not set, defaults to @"%1.f" */
  28 +@property (nonatomic, strong) NSString *yLabelFormat;
  29 +
  30 +/** Default is true. */
  31 +@property (nonatomic) BOOL showLabel;
  32 +
  33 +/** Default is 18-point Avenir Medium. */
  34 +@property (nonatomic) UIFont *descriptionTextFont;
  35 +
  36 +/** Default is white. */
  37 +@property (nonatomic) UIColor *descriptionTextColor;
  38 +
  39 +/** Default is black, with an alpha of 0.4. */
  40 +@property (nonatomic) UIColor *descriptionTextShadowColor;
  41 +
  42 +/** Default is CGSizeMake(0, 1). */
  43 +@property (nonatomic) CGSize descriptionTextShadowOffset;
  44 +
  45 +/** Default is 1.0. */
  46 +@property (nonatomic) NSTimeInterval duration;
  47 +
  48 +@property (nonatomic) CGFloat AxisX_minValue;
  49 +@property (nonatomic) CGFloat AxisX_maxValue;
  50 +
  51 +@property (nonatomic) CGFloat AxisY_minValue;
  52 +@property (nonatomic) CGFloat AxisY_maxValue;
  53 +
  54 +- (void) setAxisXWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks;
  55 +- (void) setAxisYWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks;
  56 +- (void) setup;
  57 +- (void) drawLineFromPoint : (CGPoint) startPoint ToPoint : (CGPoint) endPoint WithLineWith : (CGFloat) lineWidth AndWithColor : (UIColor*) color;
  58 +
  59 +@end
  1 +//
  2 +// PNScatterChart.m
  3 +// PNChartDemo
  4 +//
  5 +// Created by Alireza Arabi on 12/4/14.
  6 +// Copyright (c) 2014 kevinzhow. All rights reserved.
  7 +//
  8 +
  9 +#import "PNScatterChart.h"
  10 +#import "PNColor.h"
  11 +#import "PNChartLabel.h"
  12 +#import "PNScatterChartData.h"
  13 +#import "PNScatterChartDataItem.h"
  14 +
  15 +@interface PNScatterChart ()
  16 +
  17 +@property (nonatomic, weak) CAShapeLayer *pathLayer;
  18 +
  19 +@property (nonatomic) CGPoint startPoint;
  20 +
  21 +@property (nonatomic) CGPoint startPointVectorX;
  22 +@property (nonatomic) CGPoint endPointVecotrX;
  23 +
  24 +@property (nonatomic) CGPoint startPointVectorY;
  25 +@property (nonatomic) CGPoint endPointVecotrY;
  26 +
  27 +@property (nonatomic) CGFloat vectorX_Steps;
  28 +@property (nonatomic) CGFloat vectorY_Steps;
  29 +
  30 +@property (nonatomic) CGFloat vectorX_Size;
  31 +@property (nonatomic) CGFloat vectorY_Size;
  32 +
  33 +@property (nonatomic) NSMutableArray *axisX_labels;
  34 +@property (nonatomic) NSMutableArray *axisY_labels;
  35 +
  36 +@property (nonatomic) int AxisX_partNumber ;
  37 +@property (nonatomic) int AxisY_partNumber ;
  38 +
  39 +@property (nonatomic) CGFloat AxisX_step ;
  40 +@property (nonatomic) CGFloat AxisY_step ;
  41 +
  42 +@property (nonatomic) CGFloat AxisX_Margin;
  43 +@property (nonatomic) CGFloat AxisY_Margin;
  44 +
  45 +- (void)setDefaultValues;
  46 +
  47 +@end
  48 +
  49 +
  50 +@implementation PNScatterChart
  51 +
  52 +#pragma mark initialization
  53 +
  54 +- (id)initWithCoder:(NSCoder *)coder
  55 +{
  56 + self = [super initWithCoder:coder];
  57 +
  58 + if (self) {
  59 + [self setDefaultValues];
  60 + }
  61 + return self;
  62 +}
  63 +
  64 +- (id)initWithFrame:(CGRect)frame
  65 +{
  66 + self = [super initWithFrame:frame];
  67 +
  68 + if (self) {
  69 + [self setDefaultValues];
  70 + }
  71 + return self;
  72 +}
  73 +
  74 +- (void) setup
  75 +{
  76 + [self vectorXSetup];
  77 + [self vectorYSetup];
  78 +}
  79 +
  80 +- (void)setDefaultValues
  81 +{
  82 + // Initialization code
  83 + self.backgroundColor = [UIColor whiteColor];
  84 + self.clipsToBounds = YES;
  85 + _showLabel = YES;
  86 + self.userInteractionEnabled = YES;
  87 +
  88 + // Coordinate Axis Default Values
  89 + _showCoordinateAxis = YES;
  90 + _axisColor = [UIColor colorWithRed:0.4f green:0.4f blue:0.4f alpha:1.f];
  91 + _axisWidth = 1.f;
  92 +
  93 + // Initialization code
  94 + _AxisX_Margin = 30 ;
  95 + _AxisY_Margin = 30 ;
  96 +
  97 + self.frame = CGRectMake((SCREEN_WIDTH - self.frame.size.width) / 2, 200, self.frame.size.width, self.frame.size.height) ;
  98 + self.backgroundColor = [UIColor clearColor];
  99 +
  100 + _startPoint.y = self.frame.size.height - self.AxisY_Margin ;
  101 + _startPoint.x = self.AxisX_Margin ;
  102 +
  103 + _axisX_labels = [NSMutableArray array];
  104 + _axisY_labels = [NSMutableArray array];
  105 +
  106 + _descriptionTextColor = [UIColor blackColor];
  107 + _descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:9.0];
  108 + _descriptionTextShadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
  109 + _descriptionTextShadowOffset = CGSizeMake(0, 1);
  110 + _duration = 1.0;
  111 +
  112 +}
  113 +
  114 +#pragma mark calculating axis
  115 +
  116 +- (void) setAxisXWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks
  117 +{
  118 + _AxisX_minValue = minVal ;
  119 + _AxisX_maxValue = maxVal ;
  120 + _AxisX_partNumber = numberOfTicks - 1;
  121 + _AxisX_step = (float)((maxVal - minVal)/_AxisX_partNumber);
  122 +
  123 + NSString *LabelFormat = self.yLabelFormat ? : @"%1.f";
  124 + CGFloat tempValue = minVal ;
  125 + [_axisX_labels addObject:[NSString stringWithFormat:LabelFormat,minVal]];
  126 + for (int i = 0 ; i < _AxisX_partNumber; i++) {
  127 + tempValue = tempValue + _AxisX_step;
  128 + [_axisX_labels addObject:[NSString stringWithFormat:LabelFormat,tempValue]];
  129 + }
  130 +}
  131 +
  132 +- (void) setAxisYWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks
  133 +{
  134 + _AxisY_minValue = minVal ;
  135 + _AxisY_maxValue = maxVal ;
  136 + _AxisY_partNumber = numberOfTicks - 1;
  137 + _AxisY_step = (float)((maxVal - minVal)/_AxisY_partNumber);
  138 +
  139 + _axisY_labels = [NSMutableArray array];
  140 + NSString *LabelFormat = self.yLabelFormat ? : @"%1.f";
  141 + CGFloat tempValue = minVal ;
  142 + [_axisY_labels addObject:[NSString stringWithFormat:LabelFormat,minVal]];
  143 + for (int i = 0 ; i < _AxisY_partNumber; i++) {
  144 + tempValue = tempValue + _AxisY_step;
  145 + [_axisY_labels addObject:[NSString stringWithFormat:LabelFormat,tempValue]];
  146 + }
  147 +}
  148 +
  149 +- (void) vectorXSetup
  150 +{
  151 + _AxisX_partNumber += 1;
  152 + _vectorX_Size = self.frame.size.width - (_AxisX_Margin) - 15 ;
  153 + _vectorX_Steps = (_vectorX_Size) / (_AxisX_partNumber) ;
  154 + _endPointVecotrX = CGPointMake(_startPoint.x + _vectorX_Size, _startPoint.y) ;
  155 + _startPointVectorX = _startPoint ;
  156 +}
  157 +
  158 +- (void) vectorYSetup
  159 +{
  160 + _AxisY_partNumber += 1;
  161 + _vectorY_Size = self.frame.size.height - (_AxisY_Margin) - 15;
  162 + _vectorY_Steps = (_vectorY_Size) / (_AxisY_partNumber);
  163 + _endPointVecotrY = CGPointMake(_startPoint.x, _startPoint.y - _vectorY_Size) ;
  164 + _startPointVectorY = _startPoint ;
  165 +}
  166 +
  167 +- (void) showXLabelsInPosition : (CGPoint) point AndWithText : (NSString *) title
  168 +{
  169 + CGRect frame = CGRectMake(point.x, point.y, 30, 10);
  170 + UILabel *descriptionLabel = [[UILabel alloc] initWithFrame:frame];
  171 + descriptionLabel.text = title;
  172 + descriptionLabel.font = _descriptionTextFont;
  173 + descriptionLabel.textColor = _descriptionTextColor;
  174 + descriptionLabel.shadowColor = _descriptionTextShadowColor;
  175 + descriptionLabel.shadowOffset = _descriptionTextShadowOffset;
  176 + descriptionLabel.textAlignment = NSTextAlignmentCenter;
  177 + descriptionLabel.backgroundColor = [UIColor clearColor];
  178 + [self addSubview:descriptionLabel];
  179 +}
  180 +
  181 +- (void)setChartData:(NSArray *)data
  182 +{
  183 + if (data != _chartData) {
  184 + CGFloat yFinilizeValue , xFinilizeValue;
  185 + CGFloat yValue , xValue;
  186 + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
  187 + pathAnimation.duration = _duration;
  188 + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
  189 + pathAnimation.fromValue = @(0.0f);
  190 + pathAnimation.toValue = @(1.0f);
  191 + pathAnimation.fillMode = kCAFillModeForwards;
  192 + self.layer.opacity = 1;
  193 +
  194 + for (PNScatterChartData *chartData in data) {
  195 + for (NSUInteger i = 0; i < chartData.itemCount; i++) {
  196 + yValue = chartData.getData(i).y;
  197 + xValue = chartData.getData(i).x;
  198 + if (!(xValue >= _AxisX_minValue && xValue <= _AxisX_maxValue) || !(yValue >= _AxisY_minValue && yValue <= _AxisY_maxValue)) {
  199 + NSLog(@"input is not in correct range.");
  200 + exit(0);
  201 + }
  202 + xFinilizeValue = [self mappingIsForAxisX:true WithValue:xValue];
  203 + yFinilizeValue = [self mappingIsForAxisX:false WithValue:yValue];
  204 + CAShapeLayer *shape = [self drawingPointsForChartData:chartData AndWithX:xFinilizeValue AndWithY:yFinilizeValue];
  205 + [self.layer addSublayer:shape];
  206 + self.pathLayer = shape ;
  207 + [self.pathLayer addAnimation:pathAnimation forKey:@"fade"];
  208 + }
  209 + }
  210 + }
  211 +}
  212 +
  213 +- (CGFloat) mappingIsForAxisX : (BOOL) isForAxisX WithValue : (CGFloat) value{
  214 +
  215 + if (isForAxisX) {
  216 + float temp = _startPointVectorX.x + (_vectorX_Steps / 2) ;
  217 + CGFloat xPos = temp + (((value - _AxisX_minValue)/_AxisX_step) * _vectorX_Steps) ;
  218 + return xPos;
  219 + }
  220 + else {
  221 + float temp = _startPointVectorY.y - (_vectorY_Steps / 2) ;
  222 + CGFloat yPos = temp - (((value - _AxisY_minValue) /_AxisY_step) * _vectorY_Steps);
  223 + return yPos;
  224 + }
  225 + return 0;
  226 +}
  227 +
  228 +#pragma drawing methods
  229 +
  230 +- (void)drawRect:(CGRect)rect
  231 +{
  232 + [super drawRect:rect];
  233 + CGContextRef context = UIGraphicsGetCurrentContext();
  234 +
  235 + if (_showCoordinateAxis) {
  236 + CGContextSetStrokeColorWithColor(context, [_axisColor CGColor]);
  237 + CGContextSetLineWidth(context, _axisWidth);
  238 + //drawing x vector
  239 + CGContextMoveToPoint(context, _startPoint.x, _startPoint.y);
  240 + CGContextAddLineToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y);
  241 + //drawing y vector
  242 + CGContextMoveToPoint(context, _startPoint.x, _startPoint.y);
  243 + CGContextAddLineToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y);
  244 + //drawing x arrow vector
  245 + CGContextMoveToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y);
  246 + CGContextAddLineToPoint(context, _endPointVecotrX.x - 5, _endPointVecotrX.y + 3);
  247 + CGContextMoveToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y);
  248 + CGContextAddLineToPoint(context, _endPointVecotrX.x - 5, _endPointVecotrX.y - 3);
  249 + //drawing y arrow vector
  250 + CGContextMoveToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y);
  251 + CGContextAddLineToPoint(context, _endPointVecotrY.x - 3, _endPointVecotrY.y + 5);
  252 + CGContextMoveToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y);
  253 + CGContextAddLineToPoint(context, _endPointVecotrY.x + 3, _endPointVecotrY.y + 5);
  254 + }
  255 +
  256 + if (_showLabel) {
  257 + NSString *str;
  258 + //drawing x steps vector and putting axis x labels
  259 + float temp = _startPointVectorX.x + (_vectorX_Steps / 2) ;
  260 + for (int i = 0; i < _AxisX_partNumber; i++) {
  261 + CGContextMoveToPoint(context, temp, _startPointVectorX.y - 2);
  262 + CGContextAddLineToPoint(context, temp, _startPointVectorX.y + 3);
  263 + str = [_axisX_labels objectAtIndex:i];
  264 + [self showXLabelsInPosition:CGPointMake(temp - 15, _startPointVectorX.y + 10 ) AndWithText:str];
  265 + temp = temp + _vectorX_Steps ;
  266 + }
  267 + //drawing y steps vector and putting axis x labels
  268 + temp = _startPointVectorY.y - (_vectorY_Steps / 2) ;
  269 + for (int i = 0; i < _AxisY_partNumber; i++) {
  270 + CGContextMoveToPoint(context, _startPointVectorY.x - 3, temp);
  271 + CGContextAddLineToPoint(context, _startPointVectorY.x + 2, temp);
  272 + str = [_axisY_labels objectAtIndex:i];
  273 + [self showXLabelsInPosition:CGPointMake(_startPointVectorY.x - 30, temp - 5) AndWithText:str];
  274 + temp = temp - _vectorY_Steps ;
  275 + }
  276 + }
  277 + CGContextDrawPath(context, kCGPathStroke);
  278 +}
  279 +
  280 +- (CAShapeLayer*) drawingPointsForChartData : (PNScatterChartData *) chartData AndWithX : (CGFloat) X AndWithY : (CGFloat) Y
  281 +{
  282 + if (chartData.inflexionPointStyle == PNScatterChartPointStyleCircle) {
  283 + float radius = chartData.size;
  284 + CAShapeLayer *circle = [CAShapeLayer layer];
  285 + // Make a circular shape
  286 + circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(X - radius, Y - radius, 2.0*radius, 2.0*radius)
  287 + cornerRadius:radius].CGPath;
  288 + // Configure the apperence of the circle
  289 + circle.fillColor = [chartData.fillColor CGColor];
  290 + circle.strokeColor = [chartData.strokeColor CGColor];
  291 + circle.lineWidth = 1;
  292 +
  293 + // Add to parent layer
  294 + return circle;
  295 + }
  296 + else if (chartData.inflexionPointStyle == PNScatterChartPointStyleSquare) {
  297 + float side = chartData.size;
  298 + CAShapeLayer *square = [CAShapeLayer layer];
  299 + // Make a circular shape
  300 + square.path = [UIBezierPath bezierPathWithRect:CGRectMake(X - (side/2) , Y - (side/2), side, side)].CGPath ;
  301 + // Configure the apperence of the circle
  302 + square.fillColor = [chartData.fillColor CGColor];
  303 + square.strokeColor = [chartData.strokeColor CGColor];
  304 + square.lineWidth = 1;
  305 +
  306 + // Add to parent layer
  307 + return square;
  308 + }
  309 + else {
  310 + // you cann add your own scatter chart poin here
  311 + }
  312 + return nil ;
  313 +}
  314 +
  315 +- (void) drawLineFromPoint : (CGPoint) startPoint ToPoint : (CGPoint) endPoint WithLineWith : (CGFloat) lineWidth AndWithColor : (UIColor*) color{
  316 + // calculating start and end point
  317 + CGFloat startX = [self mappingIsForAxisX:true WithValue:startPoint.x];
  318 + CGFloat startY = [self mappingIsForAxisX:false WithValue:startPoint.y];
  319 + CGFloat endX = [self mappingIsForAxisX:true WithValue:endPoint.x];
  320 + CGFloat endY = [self mappingIsForAxisX:false WithValue:endPoint.y];
  321 + // drawing path between two points
  322 + UIBezierPath *path = [UIBezierPath bezierPath];
  323 + [path moveToPoint:CGPointMake(startX, startY)];
  324 + [path addLineToPoint:CGPointMake(endX, endY)];
  325 + CAShapeLayer *shapeLayer = [CAShapeLayer layer];
  326 + shapeLayer.path = [path CGPath];
  327 + shapeLayer.strokeColor = [color CGColor];
  328 + shapeLayer.lineWidth = lineWidth;
  329 + shapeLayer.fillColor = [color CGColor];
  330 + // adding animation to path
  331 + CABasicAnimation *animateStrokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
  332 + animateStrokeEnd.duration = _duration;
  333 + animateStrokeEnd.fromValue = [NSNumber numberWithFloat:0.0f];
  334 + animateStrokeEnd.toValue = [NSNumber numberWithFloat:1.0f];
  335 + [shapeLayer addAnimation:animateStrokeEnd forKey:nil];
  336 + [self.layer addSublayer:shapeLayer];
  337 +}
  338 +
  339 +@end