Merge branch 'master' of github.com:kevinzhow/PNChart
Showing
11 changed files
with
658 additions
and
3 deletions
PNChart/PNScatterChart.h
0 → 100644
| 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 | +/** | ||
| 60 | + * Update Chart Value | ||
| 61 | + */ | ||
| 62 | + | ||
| 63 | +- (void)updateChartData:(NSArray *)data; | ||
| 64 | + | ||
| 65 | +@end |
PNChart/PNScatterChart.m
0 → 100644
| 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 | +@property (nonatomic, weak) NSMutableArray *verticalLineLayer; | ||
| 19 | +@property (nonatomic, weak) NSMutableArray *horizentalLinepathLayer; | ||
| 20 | + | ||
| 21 | +@property (nonatomic) CGPoint startPoint; | ||
| 22 | + | ||
| 23 | +@property (nonatomic) CGPoint startPointVectorX; | ||
| 24 | +@property (nonatomic) CGPoint endPointVecotrX; | ||
| 25 | + | ||
| 26 | +@property (nonatomic) CGPoint startPointVectorY; | ||
| 27 | +@property (nonatomic) CGPoint endPointVecotrY; | ||
| 28 | + | ||
| 29 | +@property (nonatomic) CGFloat vectorX_Steps; | ||
| 30 | +@property (nonatomic) CGFloat vectorY_Steps; | ||
| 31 | + | ||
| 32 | +@property (nonatomic) CGFloat vectorX_Size; | ||
| 33 | +@property (nonatomic) CGFloat vectorY_Size; | ||
| 34 | + | ||
| 35 | +@property (nonatomic) NSMutableArray *axisX_labels; | ||
| 36 | +@property (nonatomic) NSMutableArray *axisY_labels; | ||
| 37 | + | ||
| 38 | +@property (nonatomic) int AxisX_partNumber ; | ||
| 39 | +@property (nonatomic) int AxisY_partNumber ; | ||
| 40 | + | ||
| 41 | +@property (nonatomic) CGFloat AxisX_step ; | ||
| 42 | +@property (nonatomic) CGFloat AxisY_step ; | ||
| 43 | + | ||
| 44 | +@property (nonatomic) CGFloat AxisX_Margin; | ||
| 45 | +@property (nonatomic) CGFloat AxisY_Margin; | ||
| 46 | + | ||
| 47 | +@property (nonatomic) BOOL isForUpdate; | ||
| 48 | + | ||
| 49 | +- (void)setDefaultValues; | ||
| 50 | + | ||
| 51 | +@end | ||
| 52 | + | ||
| 53 | + | ||
| 54 | +@implementation PNScatterChart | ||
| 55 | + | ||
| 56 | +#pragma mark initialization | ||
| 57 | + | ||
| 58 | +- (id)initWithCoder:(NSCoder *)coder | ||
| 59 | +{ | ||
| 60 | + self = [super initWithCoder:coder]; | ||
| 61 | + | ||
| 62 | + if (self) { | ||
| 63 | + [self setDefaultValues]; | ||
| 64 | + } | ||
| 65 | + return self; | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +- (id)initWithFrame:(CGRect)frame | ||
| 69 | +{ | ||
| 70 | + self = [super initWithFrame:frame]; | ||
| 71 | + | ||
| 72 | + if (self) { | ||
| 73 | + [self setDefaultValues]; | ||
| 74 | + } | ||
| 75 | + return self; | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +- (void) setup | ||
| 79 | +{ | ||
| 80 | + [self vectorXSetup]; | ||
| 81 | + [self vectorYSetup]; | ||
| 82 | +} | ||
| 83 | + | ||
| 84 | +- (void)setDefaultValues | ||
| 85 | +{ | ||
| 86 | + // Initialization code | ||
| 87 | + self.backgroundColor = [UIColor whiteColor]; | ||
| 88 | + self.clipsToBounds = YES; | ||
| 89 | + _showLabel = YES; | ||
| 90 | + _isForUpdate = NO; | ||
| 91 | + self.userInteractionEnabled = YES; | ||
| 92 | + | ||
| 93 | + // Coordinate Axis Default Values | ||
| 94 | + _showCoordinateAxis = YES; | ||
| 95 | + _axisColor = [UIColor colorWithRed:0.4f green:0.4f blue:0.4f alpha:1.f]; | ||
| 96 | + _axisWidth = 1.f; | ||
| 97 | + | ||
| 98 | + // Initialization code | ||
| 99 | + _AxisX_Margin = 30 ; | ||
| 100 | + _AxisY_Margin = 30 ; | ||
| 101 | + | ||
| 102 | +// self.frame = CGRectMake((SCREEN_WIDTH - self.frame.size.width) / 2, 200, self.frame.size.width, self.frame.size.height) ; | ||
| 103 | + self.backgroundColor = [UIColor clearColor]; | ||
| 104 | + | ||
| 105 | + _startPoint.y = self.frame.size.height - self.AxisY_Margin ; | ||
| 106 | + _startPoint.x = self.AxisX_Margin ; | ||
| 107 | + | ||
| 108 | + _axisX_labels = [NSMutableArray array]; | ||
| 109 | + _axisY_labels = [NSMutableArray array]; | ||
| 110 | + | ||
| 111 | + _descriptionTextColor = [UIColor blackColor]; | ||
| 112 | + _descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:9.0]; | ||
| 113 | + _descriptionTextShadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; | ||
| 114 | + _descriptionTextShadowOffset = CGSizeMake(0, 1); | ||
| 115 | + _duration = 1.0; | ||
| 116 | + | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +#pragma mark calculating axis | ||
| 120 | + | ||
| 121 | +- (void) setAxisXWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks | ||
| 122 | +{ | ||
| 123 | + _AxisX_minValue = minVal ; | ||
| 124 | + _AxisX_maxValue = maxVal ; | ||
| 125 | + _AxisX_partNumber = numberOfTicks - 1; | ||
| 126 | + _AxisX_step = (float)((maxVal - minVal)/_AxisX_partNumber); | ||
| 127 | + | ||
| 128 | + NSString *LabelFormat = self.yLabelFormat ? : @"%1.f"; | ||
| 129 | + CGFloat tempValue = minVal ; | ||
| 130 | + UILabel *label = [[UILabel alloc] init]; | ||
| 131 | + label.text = [NSString stringWithFormat:LabelFormat,minVal] ; | ||
| 132 | + [_axisX_labels addObject:label]; | ||
| 133 | + for (int i = 0 ; i < _AxisX_partNumber; i++) { | ||
| 134 | + tempValue = tempValue + _AxisX_step; | ||
| 135 | + UILabel *tempLabel = [[UILabel alloc] init]; | ||
| 136 | + tempLabel.text = [NSString stringWithFormat:LabelFormat,tempValue] ; | ||
| 137 | + [_axisX_labels addObject:tempLabel]; | ||
| 138 | + } | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +- (void) setAxisYWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks | ||
| 142 | +{ | ||
| 143 | + _AxisY_minValue = minVal ; | ||
| 144 | + _AxisY_maxValue = maxVal ; | ||
| 145 | + _AxisY_partNumber = numberOfTicks - 1; | ||
| 146 | + _AxisY_step = (float)((maxVal - minVal)/_AxisY_partNumber); | ||
| 147 | + | ||
| 148 | + NSString *LabelFormat = self.yLabelFormat ? : @"%1.f"; | ||
| 149 | + CGFloat tempValue = minVal ; | ||
| 150 | + UILabel *label = [[UILabel alloc] init]; | ||
| 151 | + label.text = [NSString stringWithFormat:LabelFormat,minVal] ; | ||
| 152 | + [_axisY_labels addObject:label]; | ||
| 153 | + for (int i = 0 ; i < _AxisY_partNumber; i++) { | ||
| 154 | + tempValue = tempValue + _AxisY_step; | ||
| 155 | + UILabel *tempLabel = [[UILabel alloc] init]; | ||
| 156 | + tempLabel.text = [NSString stringWithFormat:LabelFormat,tempValue] ; | ||
| 157 | + [_axisY_labels addObject:tempLabel]; | ||
| 158 | + } | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +- (void) vectorXSetup | ||
| 162 | +{ | ||
| 163 | + _AxisX_partNumber += 1; | ||
| 164 | + _vectorX_Size = self.frame.size.width - (_AxisX_Margin) - 15 ; | ||
| 165 | + _vectorX_Steps = (_vectorX_Size) / (_AxisX_partNumber) ; | ||
| 166 | + _endPointVecotrX = CGPointMake(_startPoint.x + _vectorX_Size, _startPoint.y) ; | ||
| 167 | + _startPointVectorX = _startPoint ; | ||
| 168 | +} | ||
| 169 | + | ||
| 170 | +- (void) vectorYSetup | ||
| 171 | +{ | ||
| 172 | + _AxisY_partNumber += 1; | ||
| 173 | + _vectorY_Size = self.frame.size.height - (_AxisY_Margin) - 15; | ||
| 174 | + _vectorY_Steps = (_vectorY_Size) / (_AxisY_partNumber); | ||
| 175 | + _endPointVecotrY = CGPointMake(_startPoint.x, _startPoint.y - _vectorY_Size) ; | ||
| 176 | + _startPointVectorY = _startPoint ; | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +- (void) showXLabel : (UILabel *) descriptionLabel InPosition : (CGPoint) point | ||
| 180 | +{ | ||
| 181 | + CGRect frame = CGRectMake(point.x, point.y, 30, 10); | ||
| 182 | + descriptionLabel.frame = frame; | ||
| 183 | + descriptionLabel.font = _descriptionTextFont; | ||
| 184 | + descriptionLabel.textColor = _descriptionTextColor; | ||
| 185 | + descriptionLabel.shadowColor = _descriptionTextShadowColor; | ||
| 186 | + descriptionLabel.shadowOffset = _descriptionTextShadowOffset; | ||
| 187 | + descriptionLabel.textAlignment = NSTextAlignmentCenter; | ||
| 188 | + descriptionLabel.backgroundColor = [UIColor clearColor]; | ||
| 189 | + [self addSubview:descriptionLabel]; | ||
| 190 | +} | ||
| 191 | + | ||
| 192 | +- (void)setChartData:(NSArray *)data | ||
| 193 | +{ | ||
| 194 | + __block CGFloat yFinilizeValue , xFinilizeValue; | ||
| 195 | + __block CGFloat yValue , xValue; | ||
| 196 | + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; | ||
| 197 | + pathAnimation.duration = _duration; | ||
| 198 | + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; | ||
| 199 | + pathAnimation.fromValue = @(0.0f); | ||
| 200 | + pathAnimation.toValue = @(1.0f); | ||
| 201 | + pathAnimation.fillMode = kCAFillModeForwards; | ||
| 202 | + self.layer.opacity = 1; | ||
| 203 | + | ||
| 204 | + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | ||
| 205 | + [NSThread sleepForTimeInterval:1]; | ||
| 206 | + // update UI on the main thread | ||
| 207 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 208 | + for (PNScatterChartData *chartData in data) { | ||
| 209 | + for (NSUInteger i = 0; i < chartData.itemCount; i++) { | ||
| 210 | + yValue = chartData.getData(i).y; | ||
| 211 | + xValue = chartData.getData(i).x; | ||
| 212 | + if (!(xValue >= _AxisX_minValue && xValue <= _AxisX_maxValue) || !(yValue >= _AxisY_minValue && yValue <= _AxisY_maxValue)) { | ||
| 213 | + NSLog(@"input is not in correct range."); | ||
| 214 | + exit(0); | ||
| 215 | + } | ||
| 216 | + xFinilizeValue = [self mappingIsForAxisX:true WithValue:xValue]; | ||
| 217 | + yFinilizeValue = [self mappingIsForAxisX:false WithValue:yValue]; | ||
| 218 | + CAShapeLayer *shape = [self drawingPointsForChartData:chartData AndWithX:xFinilizeValue AndWithY:yFinilizeValue]; | ||
| 219 | + self.pathLayer = shape ; | ||
| 220 | + [self.layer addSublayer:self.pathLayer]; | ||
| 221 | + [self.pathLayer addAnimation:pathAnimation forKey:@"fade"]; | ||
| 222 | + } | ||
| 223 | + } | ||
| 224 | + }); | ||
| 225 | + }); | ||
| 226 | +} | ||
| 227 | + | ||
| 228 | +- (CGFloat) mappingIsForAxisX : (BOOL) isForAxisX WithValue : (CGFloat) value{ | ||
| 229 | + | ||
| 230 | + if (isForAxisX) { | ||
| 231 | + float temp = _startPointVectorX.x + (_vectorX_Steps / 2) ; | ||
| 232 | + CGFloat xPos = temp + (((value - _AxisX_minValue)/_AxisX_step) * _vectorX_Steps) ; | ||
| 233 | + return xPos; | ||
| 234 | + } | ||
| 235 | + else { | ||
| 236 | + float temp = _startPointVectorY.y - (_vectorY_Steps / 2) ; | ||
| 237 | + CGFloat yPos = temp - (((value - _AxisY_minValue) /_AxisY_step) * _vectorY_Steps); | ||
| 238 | + return yPos; | ||
| 239 | + } | ||
| 240 | + return 0; | ||
| 241 | +} | ||
| 242 | + | ||
| 243 | +#pragma mark - Update Chart Data | ||
| 244 | + | ||
| 245 | +- (void)updateChartData:(NSArray *)data | ||
| 246 | +{ | ||
| 247 | + _chartData = data; | ||
| 248 | + | ||
| 249 | + // will be work in future. | ||
| 250 | +} | ||
| 251 | + | ||
| 252 | +#pragma drawing methods | ||
| 253 | + | ||
| 254 | +- (void)drawRect:(CGRect)rect | ||
| 255 | +{ | ||
| 256 | + [super drawRect:rect]; | ||
| 257 | + | ||
| 258 | + CGContextRef context = UIGraphicsGetCurrentContext(); | ||
| 259 | + if (_showCoordinateAxis) { | ||
| 260 | + CGContextSetStrokeColorWithColor(context, [_axisColor CGColor]); | ||
| 261 | + CGContextSetLineWidth(context, _axisWidth); | ||
| 262 | + //drawing x vector | ||
| 263 | + CGContextMoveToPoint(context, _startPoint.x, _startPoint.y); | ||
| 264 | + CGContextAddLineToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); | ||
| 265 | + //drawing y vector | ||
| 266 | + CGContextMoveToPoint(context, _startPoint.x, _startPoint.y); | ||
| 267 | + CGContextAddLineToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); | ||
| 268 | + //drawing x arrow vector | ||
| 269 | + CGContextMoveToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); | ||
| 270 | + CGContextAddLineToPoint(context, _endPointVecotrX.x - 5, _endPointVecotrX.y + 3); | ||
| 271 | + CGContextMoveToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); | ||
| 272 | + CGContextAddLineToPoint(context, _endPointVecotrX.x - 5, _endPointVecotrX.y - 3); | ||
| 273 | + //drawing y arrow vector | ||
| 274 | + CGContextMoveToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); | ||
| 275 | + CGContextAddLineToPoint(context, _endPointVecotrY.x - 3, _endPointVecotrY.y + 5); | ||
| 276 | + CGContextMoveToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); | ||
| 277 | + CGContextAddLineToPoint(context, _endPointVecotrY.x + 3, _endPointVecotrY.y + 5); | ||
| 278 | + } | ||
| 279 | + | ||
| 280 | + if (_showLabel) { | ||
| 281 | + NSString *str; | ||
| 282 | + //drawing x steps vector and putting axis x labels | ||
| 283 | + float temp = _startPointVectorX.x + (_vectorX_Steps / 2) ; | ||
| 284 | + for (int i = 0; i < _axisX_labels.count; i++) { | ||
| 285 | + UIBezierPath *path = [UIBezierPath bezierPath]; | ||
| 286 | + [path moveToPoint:CGPointMake(temp, _startPointVectorX.y - 2)]; | ||
| 287 | + [path addLineToPoint:CGPointMake(temp, _startPointVectorX.y + 3)]; | ||
| 288 | + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; | ||
| 289 | + shapeLayer.path = [path CGPath]; | ||
| 290 | + shapeLayer.strokeColor = [_axisColor CGColor]; | ||
| 291 | + shapeLayer.lineWidth = _axisWidth; | ||
| 292 | + shapeLayer.fillColor = [_axisColor CGColor]; | ||
| 293 | + [self.horizentalLinepathLayer addObject:shapeLayer]; | ||
| 294 | + [self.layer addSublayer:shapeLayer]; | ||
| 295 | + UILabel *lb = [_axisX_labels objectAtIndex:i] ; | ||
| 296 | + str = lb.text; | ||
| 297 | + [self showXLabel:lb InPosition:CGPointMake(temp - 15, _startPointVectorX.y + 10 )]; | ||
| 298 | + temp = temp + _vectorX_Steps ; | ||
| 299 | + } | ||
| 300 | + //drawing y steps vector and putting axis x labels | ||
| 301 | + temp = _startPointVectorY.y - (_vectorY_Steps / 2) ; | ||
| 302 | + for (int i = 0; i < _axisY_labels.count; i++) { | ||
| 303 | + UIBezierPath *path = [UIBezierPath bezierPath]; | ||
| 304 | + [path moveToPoint:CGPointMake(_startPointVectorY.x - 3, temp)]; | ||
| 305 | + [path addLineToPoint:CGPointMake( _startPointVectorY.x + 2, temp)]; | ||
| 306 | + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; | ||
| 307 | + shapeLayer.path = [path CGPath]; | ||
| 308 | + shapeLayer.strokeColor = [_axisColor CGColor]; | ||
| 309 | + shapeLayer.lineWidth = _axisWidth; | ||
| 310 | + shapeLayer.fillColor = [_axisColor CGColor]; | ||
| 311 | + [self.verticalLineLayer addObject:shapeLayer]; | ||
| 312 | + [self.layer addSublayer:shapeLayer]; | ||
| 313 | + UILabel *lb = [_axisY_labels objectAtIndex:i]; | ||
| 314 | + str = lb.text; | ||
| 315 | + [self showXLabel:lb InPosition:CGPointMake(_startPointVectorY.x - 30, temp - 5)]; | ||
| 316 | + temp = temp - _vectorY_Steps ; | ||
| 317 | + } | ||
| 318 | + } | ||
| 319 | + CGContextDrawPath(context, kCGPathStroke); | ||
| 320 | +} | ||
| 321 | + | ||
| 322 | +- (CAShapeLayer*) drawingPointsForChartData : (PNScatterChartData *) chartData AndWithX : (CGFloat) X AndWithY : (CGFloat) Y | ||
| 323 | +{ | ||
| 324 | + if (chartData.inflexionPointStyle == PNScatterChartPointStyleCircle) { | ||
| 325 | + float radius = chartData.size; | ||
| 326 | + CAShapeLayer *circle = [CAShapeLayer layer]; | ||
| 327 | + // Make a circular shape | ||
| 328 | + circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(X - radius, Y - radius, 2.0*radius, 2.0*radius) | ||
| 329 | + cornerRadius:radius].CGPath; | ||
| 330 | + // Configure the apperence of the circle | ||
| 331 | + circle.fillColor = [chartData.fillColor CGColor]; | ||
| 332 | + circle.strokeColor = [chartData.strokeColor CGColor]; | ||
| 333 | + circle.lineWidth = 1; | ||
| 334 | + | ||
| 335 | + // Add to parent layer | ||
| 336 | + return circle; | ||
| 337 | + } | ||
| 338 | + else if (chartData.inflexionPointStyle == PNScatterChartPointStyleSquare) { | ||
| 339 | + float side = chartData.size; | ||
| 340 | + CAShapeLayer *square = [CAShapeLayer layer]; | ||
| 341 | + // Make a circular shape | ||
| 342 | + square.path = [UIBezierPath bezierPathWithRect:CGRectMake(X - (side/2) , Y - (side/2), side, side)].CGPath ; | ||
| 343 | + // Configure the apperence of the circle | ||
| 344 | + square.fillColor = [chartData.fillColor CGColor]; | ||
| 345 | + square.strokeColor = [chartData.strokeColor CGColor]; | ||
| 346 | + square.lineWidth = 1; | ||
| 347 | + | ||
| 348 | + // Add to parent layer | ||
| 349 | + return square; | ||
| 350 | + } | ||
| 351 | + else { | ||
| 352 | + // you cann add your own scatter chart poin here | ||
| 353 | + } | ||
| 354 | + return nil ; | ||
| 355 | +} | ||
| 356 | + | ||
| 357 | +- (void) drawLineFromPoint : (CGPoint) startPoint ToPoint : (CGPoint) endPoint WithLineWith : (CGFloat) lineWidth AndWithColor : (UIColor*) color{ | ||
| 358 | + | ||
| 359 | + // call the same method on a background thread | ||
| 360 | + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | ||
| 361 | + [NSThread sleepForTimeInterval:2]; | ||
| 362 | + // calculating start and end point | ||
| 363 | + __block CGFloat startX = [self mappingIsForAxisX:true WithValue:startPoint.x]; | ||
| 364 | + __block CGFloat startY = [self mappingIsForAxisX:false WithValue:startPoint.y]; | ||
| 365 | + __block CGFloat endX = [self mappingIsForAxisX:true WithValue:endPoint.x]; | ||
| 366 | + __block CGFloat endY = [self mappingIsForAxisX:false WithValue:endPoint.y]; | ||
| 367 | + // update UI on the main thread | ||
| 368 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 369 | + // drawing path between two points | ||
| 370 | + UIBezierPath *path = [UIBezierPath bezierPath]; | ||
| 371 | + [path moveToPoint:CGPointMake(startX, startY)]; | ||
| 372 | + [path addLineToPoint:CGPointMake(endX, endY)]; | ||
| 373 | + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; | ||
| 374 | + shapeLayer.path = [path CGPath]; | ||
| 375 | + shapeLayer.strokeColor = [color CGColor]; | ||
| 376 | + shapeLayer.lineWidth = lineWidth; | ||
| 377 | + shapeLayer.fillColor = [color CGColor]; | ||
| 378 | + // adding animation to path | ||
| 379 | + CABasicAnimation *animateStrokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; | ||
| 380 | + animateStrokeEnd.duration = _duration; | ||
| 381 | + animateStrokeEnd.fromValue = [NSNumber numberWithFloat:0.0f]; | ||
| 382 | + animateStrokeEnd.toValue = [NSNumber numberWithFloat:1.0f]; | ||
| 383 | + [shapeLayer addAnimation:animateStrokeEnd forKey:nil]; | ||
| 384 | + [self.layer addSublayer:shapeLayer]; | ||
| 385 | + }); | ||
| 386 | + }); | ||
| 387 | +} | ||
| 388 | + | ||
| 389 | +@end |
PNChart/PNScatterChartData.h
0 → 100644
| 1 | +// | ||
| 2 | +// PNScatterChartData.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 <Foundation/Foundation.h> | ||
| 10 | + | ||
| 11 | +typedef NS_ENUM(NSUInteger, PNScatterChartPointStyle) { | ||
| 12 | + PNScatterChartPointStyleCircle = 0, | ||
| 13 | + PNScatterChartPointStyleSquare = 1, | ||
| 14 | +}; | ||
| 15 | + | ||
| 16 | +@class PNScatterChartDataItem; | ||
| 17 | + | ||
| 18 | +typedef PNScatterChartDataItem *(^LCScatterChartDataGetter)(NSUInteger item); | ||
| 19 | + | ||
| 20 | +@interface PNScatterChartData : NSObject | ||
| 21 | + | ||
| 22 | +@property (strong) UIColor *fillColor; | ||
| 23 | +@property (strong) UIColor *strokeColor; | ||
| 24 | + | ||
| 25 | +@property NSUInteger itemCount; | ||
| 26 | +@property (copy) LCScatterChartDataGetter getData; | ||
| 27 | + | ||
| 28 | +@property (nonatomic, assign) PNScatterChartPointStyle inflexionPointStyle; | ||
| 29 | + | ||
| 30 | +/** | ||
| 31 | + * If PNLineChartPointStyle is circle, this returns the circle's diameter. | ||
| 32 | + * If PNLineChartPointStyle is square, each point is a square with each side equal in length to this value. | ||
| 33 | + */ | ||
| 34 | +@property (nonatomic, assign) CGFloat size; | ||
| 35 | + | ||
| 36 | + | ||
| 37 | +@end |
PNChart/PNScatterChartData.m
0 → 100644
| 1 | +// | ||
| 2 | +// PNScatterChartData.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 "PNScatterChartData.h" | ||
| 10 | + | ||
| 11 | +@implementation PNScatterChartData | ||
| 12 | + | ||
| 13 | +- (id)init | ||
| 14 | +{ | ||
| 15 | + self = [super init]; | ||
| 16 | + if (self) { | ||
| 17 | + [self setDefaultValues]; | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + return self; | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +- (void)setDefaultValues | ||
| 24 | +{ | ||
| 25 | + _inflexionPointStyle = PNScatterChartPointStyleCircle; | ||
| 26 | + _fillColor = [UIColor grayColor]; | ||
| 27 | + _strokeColor = [UIColor clearColor]; | ||
| 28 | + _size = 3 ; | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +@end |
PNChart/PNScatterChartDataItem.h
0 → 100644
| 1 | +// | ||
| 2 | +// PNScatterChartDataItem.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 <Foundation/Foundation.h> | ||
| 10 | + | ||
| 11 | +@interface PNScatterChartDataItem : NSObject | ||
| 12 | + | ||
| 13 | ++ (PNScatterChartDataItem *)dataItemWithX:(CGFloat)x AndWithY:(CGFloat)y; | ||
| 14 | + | ||
| 15 | +@property (readonly) CGFloat x; // should be within the x range | ||
| 16 | +@property (readonly) CGFloat y; // should be within the y range | ||
| 17 | + | ||
| 18 | +@end |
PNChart/PNScatterChartDataItem.m
0 → 100644
| 1 | +// | ||
| 2 | +// PNScatterChartDataItem.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 "PNScatterChartDataItem.h" | ||
| 10 | + | ||
| 11 | +@interface PNScatterChartDataItem () | ||
| 12 | + | ||
| 13 | +- (id)initWithX:(CGFloat)x AndWithY:(CGFloat)y; | ||
| 14 | + | ||
| 15 | +@property (readwrite) CGFloat x; // should be within the x range | ||
| 16 | +@property (readwrite) CGFloat y; // should be within the y range | ||
| 17 | + | ||
| 18 | +@end | ||
| 19 | + | ||
| 20 | +@implementation PNScatterChartDataItem | ||
| 21 | + | ||
| 22 | ++ (PNScatterChartDataItem *)dataItemWithX:(CGFloat)x AndWithY:(CGFloat)y | ||
| 23 | +{ | ||
| 24 | + return [[PNScatterChartDataItem alloc] initWithX:x AndWithY:y]; | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +- (id)initWithX:(CGFloat)x AndWithY:(CGFloat)y | ||
| 28 | +{ | ||
| 29 | + if ((self = [super init])) { | ||
| 30 | + self.x = x; | ||
| 31 | + self.y = y; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + return self; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +@end |
| @@ -142,6 +142,24 @@ | @@ -142,6 +142,24 @@ | ||
| 142 | <segue destination="Tha-Wr-sPW" kind="push" identifier="pieChart" id="pvQ-oy-a9a"/> | 142 | <segue destination="Tha-Wr-sPW" kind="push" identifier="pieChart" id="pvQ-oy-a9a"/> |
| 143 | </connections> | 143 | </connections> |
| 144 | </tableViewCell> | 144 | </tableViewCell> |
| 145 | + <tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" indentationLevel="1" indentationWidth="0.0" textLabel="YOU-SK-mQU" style="IBUITableViewCellStyleDefault" id="JJR-oU-C7n"> | ||
| 146 | + <rect key="frame" x="0.0" y="0.0" width="320" height="44"/> | ||
| 147 | + <autoresizingMask key="autoresizingMask"/> | ||
| 148 | + <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="JJR-oU-C7n" id="iJk-3W-tcy"> | ||
| 149 | + <autoresizingMask key="autoresizingMask"/> | ||
| 150 | + <subviews> | ||
| 151 | + <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="ScatterChart" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="YOU-SK-mQU"> | ||
| 152 | + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> | ||
| 153 | + <fontDescription key="fontDescription" type="system" pointSize="18"/> | ||
| 154 | + <color key="textColor" cocoaTouchSystemColor="darkTextColor"/> | ||
| 155 | + <nil key="highlightedColor"/> | ||
| 156 | + </label> | ||
| 157 | + </subviews> | ||
| 158 | + </tableViewCellContentView> | ||
| 159 | + <connections> | ||
| 160 | + <segue destination="Tha-Wr-sPW" kind="push" identifier="scatterChart" id="V7s-JV-4Nx"/> | ||
| 161 | + </connections> | ||
| 162 | + </tableViewCell> | ||
| 145 | </cells> | 163 | </cells> |
| 146 | </tableViewSection> | 164 | </tableViewSection> |
| 147 | </sections> | 165 | </sections> |
| @@ -174,6 +192,6 @@ | @@ -174,6 +192,6 @@ | ||
| 174 | <simulatedScreenMetrics key="destination" type="retina4"/> | 192 | <simulatedScreenMetrics key="destination" type="retina4"/> |
| 175 | </simulatedMetricsContainer> | 193 | </simulatedMetricsContainer> |
| 176 | <inferredMetricsTieBreakers> | 194 | <inferredMetricsTieBreakers> |
| 177 | - <segue reference="pvQ-oy-a9a"/> | 195 | + <segue reference="V7s-JV-4Nx"/> |
| 178 | </inferredMetricsTieBreakers> | 196 | </inferredMetricsTieBreakers> |
| 179 | </document> | 197 | </document> |
| @@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
| 16 | @property (nonatomic) PNBarChart * barChart; | 16 | @property (nonatomic) PNBarChart * barChart; |
| 17 | @property (nonatomic) PNCircleChart * circleChart; | 17 | @property (nonatomic) PNCircleChart * circleChart; |
| 18 | @property (nonatomic) PNPieChart *pieChart; | 18 | @property (nonatomic) PNPieChart *pieChart; |
| 19 | +@property (nonatomic) PNScatterChart *scatterChart; | ||
| 19 | 20 | ||
| 20 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; | 21 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; |
| 21 | 22 |
| @@ -7,6 +7,7 @@ | @@ -7,6 +7,7 @@ | ||
| 7 | // | 7 | // |
| 8 | 8 | ||
| 9 | #import "PCChartViewController.h" | 9 | #import "PCChartViewController.h" |
| 10 | +#define ARC4RANDOM_MAX 0x100000000 | ||
| 10 | 11 | ||
| 11 | @implementation PCChartViewController | 12 | @implementation PCChartViewController |
| 12 | 13 | ||
| @@ -120,6 +121,41 @@ | @@ -120,6 +121,41 @@ | ||
| 120 | [self.view addSubview:self.pieChart]; | 121 | [self.view addSubview:self.pieChart]; |
| 121 | self.changeValueButton.hidden = YES; | 122 | self.changeValueButton.hidden = YES; |
| 122 | } | 123 | } |
| 124 | + else if ([self.title isEqualToString:@"Scatter Chart"]) | ||
| 125 | + { | ||
| 126 | + self.titleLabel.text = @"Scatter Chart"; | ||
| 127 | + | ||
| 128 | + self.scatterChart = [[PNScatterChart alloc] initWithFrame:CGRectMake(SCREEN_WIDTH /6.0 - 30, 135, 280, 200)]; | ||
| 129 | + [self.scatterChart setAxisXWithMinimumValue:20 andMaxValue:100 toTicks:6]; | ||
| 130 | + [self.scatterChart setAxisYWithMinimumValue:30 andMaxValue:50 toTicks:5]; | ||
| 131 | + | ||
| 132 | + NSArray * data01Array = [self randomSetOfObjects]; | ||
| 133 | + PNScatterChartData *data01 = [PNScatterChartData new]; | ||
| 134 | + data01.strokeColor = PNGreen; | ||
| 135 | + data01.fillColor = PNFreshGreen; | ||
| 136 | + data01.size = 2; | ||
| 137 | + data01.itemCount = [[data01Array objectAtIndex:0] count]; | ||
| 138 | + data01.inflexionPointStyle = PNScatterChartPointStyleCircle; | ||
| 139 | + __block NSMutableArray *XAr1 = [NSMutableArray arrayWithArray:[data01Array objectAtIndex:0]]; | ||
| 140 | + __block NSMutableArray *YAr1 = [NSMutableArray arrayWithArray:[data01Array objectAtIndex:1]]; | ||
| 141 | + data01.getData = ^(NSUInteger index) { | ||
| 142 | + CGFloat xValue = [[XAr1 objectAtIndex:index] floatValue]; | ||
| 143 | + CGFloat yValue = [[YAr1 objectAtIndex:index] floatValue]; | ||
| 144 | + return [PNScatterChartDataItem dataItemWithX:xValue AndWithY:yValue]; | ||
| 145 | + }; | ||
| 146 | + | ||
| 147 | + [self.scatterChart setup]; | ||
| 148 | + self.scatterChart.chartData = @[data01]; | ||
| 149 | +/*** | ||
| 150 | + this is for drawing line to compare | ||
| 151 | + CGPoint start = CGPointMake(20, 35); | ||
| 152 | + CGPoint end = CGPointMake(80, 45); | ||
| 153 | + [self.scatterChart drawLineFromPoint:start ToPoint:end WithLineWith:2 AndWithColor:PNBlack]; | ||
| 154 | +***/ | ||
| 155 | + self.scatterChart.delegate = self; | ||
| 156 | + self.changeValueButton.hidden = YES; | ||
| 157 | + [self.view addSubview:self.scatterChart]; | ||
| 158 | + } | ||
| 123 | } | 159 | } |
| 124 | 160 | ||
| 125 | 161 | ||
| @@ -171,6 +207,10 @@ | @@ -171,6 +207,10 @@ | ||
| 171 | { | 207 | { |
| 172 | [self.circleChart updateChartByCurrent:@(arc4random() % 100)]; | 208 | [self.circleChart updateChartByCurrent:@(arc4random() % 100)]; |
| 173 | } | 209 | } |
| 210 | + else if ([self.title isEqualToString:@"Scatter Chart"]) | ||
| 211 | + { | ||
| 212 | + // will be code soon. | ||
| 213 | + } | ||
| 174 | 214 | ||
| 175 | } | 215 | } |
| 176 | 216 | ||
| @@ -195,4 +235,19 @@ | @@ -195,4 +235,19 @@ | ||
| 195 | [bar.layer addAnimation:animation forKey:@"Float"]; | 235 | [bar.layer addAnimation:animation forKey:@"Float"]; |
| 196 | } | 236 | } |
| 197 | 237 | ||
| 238 | +/* this function is used only for creating random points */ | ||
| 239 | +- (NSArray *) randomSetOfObjects{ | ||
| 240 | + NSMutableArray *array = [NSMutableArray array]; | ||
| 241 | + NSString *LabelFormat = @"%1.f"; | ||
| 242 | + NSMutableArray *XAr = [NSMutableArray array]; | ||
| 243 | + NSMutableArray *YAr = [NSMutableArray array]; | ||
| 244 | + for (int i = 0; i < 25 ; i++) { | ||
| 245 | + [XAr addObject:[NSString stringWithFormat:LabelFormat,(((double)arc4random() / ARC4RANDOM_MAX) * (self.scatterChart.AxisX_maxValue - self.scatterChart.AxisX_minValue) + self.scatterChart.AxisX_minValue)]]; | ||
| 246 | + [YAr addObject:[NSString stringWithFormat:LabelFormat,(((double)arc4random() / ARC4RANDOM_MAX) * (self.scatterChart.AxisY_maxValue - self.scatterChart.AxisY_minValue) + self.scatterChart.AxisY_minValue)]]; | ||
| 247 | + } | ||
| 248 | + [array addObject:XAr]; | ||
| 249 | + [array addObject:YAr]; | ||
| 250 | + return (NSArray*) array; | ||
| 251 | +} | ||
| 252 | + | ||
| 198 | @end | 253 | @end |
| @@ -40,9 +40,12 @@ | @@ -40,9 +40,12 @@ | ||
| 40 | //Add pie chart | 40 | //Add pie chart |
| 41 | 41 | ||
| 42 | viewController.title = @"Pie Chart"; | 42 | viewController.title = @"Pie Chart"; |
| 43 | + } else if ([segue.identifier isEqualToString:@"scatterChart"]) | ||
| 44 | + { | ||
| 45 | + //Add scatter chart | ||
| 46 | + | ||
| 47 | + viewController.title = @"Scatter Chart"; | ||
| 43 | } | 48 | } |
| 44 | } | 49 | } |
| 45 | 50 | ||
| 46 | - | ||
| 47 | - | ||
| 48 | @end | 51 | @end |
-
Please register or login to post a comment