From 764722d0366346dc435aa906abdd25655e3b0769 Mon Sep 17 00:00:00 2001 From: 单军华 <WindShan@danjunhuas-MacBook-Pro.local> Date: Fri, 03 Mar 2017 09:54:19 +0800 Subject: [PATCH] 图形绘制demo --- PNChartdemo/PNChartdemo/PNChart/PNChartDelegate.h | 33 PNChartdemo/PNChartdemo/UICountingLabel/UICountingLabel.m | 234 + PNChartdemo/PNChart/PNScatterChart.m | 445 ++ PNChartdemo/PNChartdemo/PNChart/PNScatterChart.h | 69 PNChartdemo/PNChart/PNPieChart.h | 68 PNChartdemo/PNChart/PNChart.h | 22 PNChartdemo/PNChart/PNScatterChart.h | 69 PNChartdemo/PNChart/PNLineChart.m | 1285 ++++++ PNChartdemo/PNChart/PNLineChartDataItem.m | 38 PNChartdemo/PNChart/PNPieChart.m | 508 ++ PNChartdemo/PNChartdemo/PCChartViewController.m | 490 ++ PNChartdemo/PNChartdemo/PNChart/PNScatterChartDataItem.m | 37 PNChartdemo/PNChart/PNLineChartDataItem.h | 17 PNChartdemo/PNChart/PNLineChart.h | 110 PNChartdemo/PNChart/PNScatterChartData.h | 38 PNChartdemo/PNChartdemo/PCChartViewController.h | 38 PNChartdemo/PNChartdemo/PNChart/PNLineChart.h | 110 PNChartdemo/PNChart/PNBar.h | 37 PNChartdemo/PNChartdemo/ViewController.m | 4 PNChartdemo/PNChartdemo/AppDelegate.m | 4 PNChartdemo/PNChart/PNBar.m | 288 + PNChartdemo/PNChartdemo/ViewController.h | 4 PNChartdemo/PNChartdemo/AppDelegate.h | 4 PNChartdemo/PNChartdemo/UICountingLabel/UICountingLabel.h | 36 PNChartdemo/PNChart/PNRadarChart.h | 52 PNChartdemo/PNChartdemo/Base.lproj/Main.storyboard | 300 + PNChartdemo/PNChartdemo/PNChart/PNPieChartDataItem.h | 25 PNChartdemo/PNChart/PNCircleChart.m | 267 + PNChartdemo/PNChart/PNBarChart.m | 459 ++ PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/PNChartdemo.xcscheme | 91 PNChartdemo/PNChartdemo/PNChart/PNScatterChartDataItem.h | 19 PNChartdemo/PNChartdemo/PNChart/PNColor.h | 53 PNChartdemo/PNChartdemo/PNChart/PNPieChart.h | 68 PNChartdemo/PNChart/PNCircleChart.h | 74 PNChartdemo/PNChartdemo/PNChart/PNColor.m | 29 PNChartdemo/PNChartdemo/PNChart/PNChartLabel.m | 32 PNChartdemo/PNChartdemo/PNChart/PNPieChartDataItem.m | 38 PNChartdemo/PNChartdemo/PNChart/PNPieChart.m | 508 ++ PNChartdemo/PNChart/PNGenericChart.m | 54 PNChartdemo/PNChartdemo/PNChart/PNScatterChart.m | 445 ++ PNChartdemo/PNChartdemo/PNChart/PNChartLabel.h | 13 PNChartdemo/PNChartdemo/PNChart/PNLineChartDataItem.h | 17 PNChartdemo/PNChart/PNRadarChart.m | 373 + PNChartdemo/PNChart/PNGenericChart.h | 48 PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist | 55 PNChartdemo/PNChartdemo/PNChart/PNBarChart.h | 123 PNChartdemo/PNChartdemo/PNChart/PNGenericChart.m | 54 PNChartdemo/PNChart/PNColor.h | 53 PNChartdemo/PNChartdemo/PNChart/PNBarChart.m | 459 ++ PNChartdemo/PNChartdemo/PNChart/PNLineChartDataItem.m | 38 PNChartdemo/PNChart/PNColor.m | 29 PNChartdemo/PNChartdemo/PNChart/PNChart.h | 22 PNChartdemo/PNChartdemo.xcodeproj/project.pbxproj | 479 ++ PNChartdemo/PNChartdemo/PNChart/PNGenericChart.h | 48 PNChartdemo/PNChart/PNPieChartDataItem.m | 38 PNChartdemo/PNChartdemo/Info.plist | 0 PNChartdemo/PNChart/PNBarChart.h | 123 PNChartdemo/PNChart/PNPieChartDataItem.h | 25 PNChartdemo/PNChartdemo/PCChartsTableViewController.h | 13 PNChartdemo/PNChartdemo/PNChartDemo-Prefix.pch | 16 PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/xcschememanagement.plist | 4 PNChartdemo/PNChartdemo/PCChartsTableViewController.m | 56 PNChartdemo/PNChartdemo/PNChart/PNRadarChart.h | 52 PNChartdemo/PNChartdemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata | 2 PNChartdemo/PNChartdemo/PNChart/PNCircleChart.h | 74 PNChartdemo/PNChart/PNRadarChartDataItem.m | 29 PNChartdemo/PNChartdemo/Base.lproj/LaunchScreen.storyboard | 0 PNChartdemo/PNChart/PNChartLabel.m | 32 PNChartdemo/PNChartdemo/PNChart/PNRadarChart.m | 373 + PNChartdemo/PNChart/PNRadarChartDataItem.h | 19 PNChartdemo/PNChartdemo/PNChart/PNCircleChart.m | 267 + PNChartdemo/PNChart/PNChartLabel.h | 13 PNChartdemo/PNChartdemo/PNChart/PNLineChart.m | 1284 ++++++ PNChartdemo/PNChartdemo.xcodeproj/project.xcworkspace/xcuserdata/WindShan.xcuserdatad/UserInterfaceState.xcuserstate | 0 PNChartdemo/PNChart/PNChartDelegate.h | 33 PNChartdemo/PNChart/PNScatterChartData.m | 31 PNChartdemo/PNChartdemo/PNChart/PNBar.h | 37 PNChartdemo/PNChartdemo/PNChart/PNScatterChartData.m | 31 /dev/null | 114 PNChartdemo/PNChart/PNLineChartData.m | 54 PNChartdemo/PNChart/PNScatterChartDataItem.h | 19 PNChartdemo/PNChartdemo/main.m | 4 PNChartdemo/PNChartdemo/PNChart/PNBar.m | 288 + PNChartdemo/PNChartdemo/PNChart/PNRadarChartDataItem.m | 29 PNChartdemo/PNChart/PNScatterChartDataItem.m | 37 PNChartdemo/PNChartdemo/PNChart/PNScatterChartData.h | 38 PNChartdemo/PNChartdemo/PNChart/PNRadarChartDataItem.h | 19 PNChartdemo/PNChartdemo/Assets.xcassets/AppIcon.appiconset/Contents.json | 0 PNChartdemo/PNChartdemo/PNChart/PNLineChartData.m | 54 PNChartdemo/PNChartdemo/PNChart/PNLineChartData.h | 61 PNChartdemo/PNChart/PNLineChartData.h | 61 91 files changed, 11,516 insertions(+), 127 deletions(-) diff --git a/PNChartdemo/PNChart/PNBar.h b/PNChartdemo/PNChart/PNBar.h new file mode 100755 index 0000000..68e6b0b --- /dev/null +++ b/PNChartdemo/PNChart/PNBar.h @@ -0,0 +1,37 @@ +// +// PNBar.h +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import <QuartzCore/QuartzCore.h> + +@interface PNBar : UIView + + +- (void)rollBack; + +@property (nonatomic) float grade; +@property (nonatomic) float maxDivisor; + +@property (nonatomic) CAShapeLayer *chartLine; +@property (nonatomic) UIColor *barColor; +@property (nonatomic) UIColor *barColorGradientStart; +@property (nonatomic) CGFloat barRadius; +@property (nonatomic) CAShapeLayer *gradientMask; + +@property (nonatomic) CAShapeLayer *gradeLayer; +@property (nonatomic) CATextLayer* textLayer; + +/** Text color for all bars in the chart. */ +@property (nonatomic) UIColor * labelTextColor; + +@property (nonatomic, assign) BOOL isNegative; //!< ��������������� +@property (nonatomic, assign) BOOL isShowNumber; //!< ������������numbers + +/** Display the bar with or without animation. Default is YES. **/ +@property (nonatomic) BOOL displayAnimated; +@end diff --git a/PNChartdemo/PNChart/PNBar.m b/PNChartdemo/PNChart/PNBar.m new file mode 100755 index 0000000..8ea5fc9 --- /dev/null +++ b/PNChartdemo/PNChart/PNBar.m @@ -0,0 +1,288 @@ +// +// PNBar.m +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNBar.h" +#import "PNColor.h" +#import <CoreText/CoreText.h> + +@interface PNBar () + +@property (nonatomic) float copyGrade; + +@end + +@implementation PNBar + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + _chartLine = [CAShapeLayer layer]; + _chartLine.lineCap = kCALineCapButt; + _chartLine.fillColor = [[UIColor whiteColor] CGColor]; + _chartLine.lineWidth = self.frame.size.width; + _chartLine.strokeEnd = 0.0; + self.clipsToBounds = YES; + [self.layer addSublayer:_chartLine]; + self.barRadius = 2.0; + } + + return self; +} + +-(void)setBarRadius:(CGFloat)barRadius +{ + _barRadius = barRadius; + self.layer.cornerRadius = _barRadius; +} + + +- (void)setGrade:(float)grade +{ + _copyGrade = grade; + CGFloat startPosY = (1 - grade) * self.frame.size.height; + + UIBezierPath *progressline = [UIBezierPath bezierPath]; + + [progressline moveToPoint:CGPointMake(self.frame.size.width / 2.0, self.frame.size.height)]; + [progressline addLineToPoint:CGPointMake(self.frame.size.width / 2.0, startPosY)]; + + [progressline setLineWidth:1.0]; + [progressline setLineCapStyle:kCGLineCapSquare]; + [self addAnimationIfNeededWithProgressLine:progressline]; + + + if (_barColor) { + _chartLine.strokeColor = [_barColor CGColor]; + } + else { + _chartLine.strokeColor = [PNGreen CGColor]; + } + + if (_grade) { + + _chartLine.path = progressline.CGPath; + + if (_barColorGradientStart) { + + // Add gradient + self.gradientMask.path = progressline.CGPath; + + CABasicAnimation* opacityAnimation = [self fadeAnimation]; + [self.textLayer addAnimation:opacityAnimation forKey:nil]; + + } + + }else{ + _chartLine.strokeEnd = 1.0; + + _chartLine.path = progressline.CGPath; + // Check if user wants to add a gradient from the start color to the bar color + if (_barColorGradientStart) { + + // Add gradient + self.gradientMask = [CAShapeLayer layer]; + self.gradientMask.fillColor = [[UIColor clearColor] CGColor]; + self.gradientMask.strokeColor = [[UIColor blackColor] CGColor]; + self.gradientMask.lineWidth = self.frame.size.width; + self.gradientMask.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height); + self.gradientMask.path = progressline.CGPath; + + CAGradientLayer *gradientLayer = [CAGradientLayer layer]; + gradientLayer.startPoint = CGPointMake(0.0,0.0); + gradientLayer.endPoint = CGPointMake(1.0 ,0.0); + gradientLayer.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height); + UIColor *middleColor = [UIColor colorWithWhite:255/255 alpha:0.8]; + NSArray *colors = @[ + (__bridge id)self.barColor.CGColor, + (__bridge id)middleColor.CGColor, + (__bridge id)self.barColor.CGColor + ]; + gradientLayer.colors = colors; + + [gradientLayer setMask:self.gradientMask]; + + [_chartLine addSublayer:gradientLayer]; + + self.gradientMask.strokeEnd = 1.0; + + CABasicAnimation* opacityAnimation = [self fadeAnimation]; + [self.textLayer addAnimation:opacityAnimation forKey:nil]; + } + } + + _grade = grade; + +} + + +- (void)rollBack +{ + [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations: ^{ + _chartLine.strokeColor = [UIColor clearColor].CGColor; + } completion:nil]; +} + +- (void)setBarColorGradientStart:(UIColor *)barColorGradientStart +{ + // Set gradient color, remove any existing sublayer first + for (CALayer *sublayer in [_chartLine sublayers]) { + [sublayer removeFromSuperlayer]; + } + _barColorGradientStart = barColorGradientStart; + + [self setGrade:_grade]; + +} + +// Only override drawRect: if you perform custom drawing. +// An empty implementation adversely affects performance during animation. +- (void)drawRect:(CGRect)rect +{ + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor); + CGContextFillRect(context, rect); +} + + +// add number display on the top of bar +-(CGPathRef)gradePath:(CGRect)rect +{ + return nil; +} + +-(CATextLayer*)textLayer +{ + if (!_textLayer) { + _textLayer = [[CATextLayer alloc]init]; + [_textLayer setString:@"0"]; + [_textLayer setAlignmentMode:kCAAlignmentCenter]; + [_textLayer setForegroundColor:[_labelTextColor CGColor]]; + _textLayer.hidden = YES; + + } + + return _textLayer; +} + +- (void) setLabelTextColor:(UIColor *)labelTextColor { + _labelTextColor = labelTextColor; + [_textLayer setForegroundColor:[_labelTextColor CGColor]]; +} + +-(void)setGradeFrame:(CGFloat)grade startPosY:(CGFloat)startPosY +{ + CGFloat textheigt = self.bounds.size.height*self.grade; + + CGFloat topSpace = self.bounds.size.height * (1-self.grade); + CGFloat textWidth = self.bounds.size.width; + + [_chartLine addSublayer:self.textLayer]; + [self.textLayer setFontSize:18.0]; + + [self.textLayer setString:[[NSString alloc]initWithFormat:@"%0.f",grade*self.maxDivisor]]; + + CGSize size = CGSizeMake(320,2000); //������������������������ + NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:18.0]}; + size = [self.textLayer.string boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size; + float verticalY ; + + if (size.height>=textheigt) { + + verticalY = topSpace - size.height; + } else { + verticalY = topSpace + (textheigt-size.height)/2.0; + } + + [self.textLayer setFrame:CGRectMake((textWidth-size.width)/2.0,verticalY, size.width,size.height)]; + self.textLayer.contentsScale = [UIScreen mainScreen].scale; + +} + +- (void)setIsShowNumber:(BOOL)isShowNumber{ + if (isShowNumber) { + self.textLayer.hidden = NO; + [self setGradeFrame:_copyGrade startPosY:0]; + }else{ + self.textLayer.hidden = YES; + } +} +- (void)setIsNegative:(BOOL)isNegative{ + if (isNegative) { + [self.textLayer setString:[[NSString alloc]initWithFormat:@"- %1.f",_grade*self.maxDivisor]]; + + CGSize size = CGSizeMake(320,2000); //������������������������ + NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:18.0]}; + size = [self.textLayer.string boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size; + CGRect frame = self.textLayer.frame; + frame.origin.x = (self.bounds.size.width - size.width)/2.0; + frame.size = size; + self.textLayer.frame = frame; + + [self addRotationAnimationIfNeeded]; + } +} + +-(CABasicAnimation*)fadeAnimation +{ + CABasicAnimation* fadeAnimation = nil; + if (self.displayAnimated) { + fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + fadeAnimation.fromValue = [NSNumber numberWithFloat:0.0]; + fadeAnimation.toValue = [NSNumber numberWithFloat:1.0]; + fadeAnimation.duration = 2.0; + } + return fadeAnimation; +} + +-(void)addAnimationIfNeededWithProgressLine:(UIBezierPath *)progressline +{ + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = nil; + + if (_grade) { + pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; + pathAnimation.fromValue = (id)_chartLine.path; + pathAnimation.toValue = (id)[progressline CGPath]; + pathAnimation.duration = 0.5f; + pathAnimation.autoreverses = NO; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + [_chartLine addAnimation:pathAnimation forKey:@"animationKey"]; + } + else { + pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathAnimation.duration = 1.0; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @0.0f; + pathAnimation.toValue = @1.0f; + [_chartLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + } + + [self.gradientMask addAnimation:pathAnimation forKey:@"animationKey"]; + } +} + +- (void)addRotationAnimationIfNeeded +{ + if (self.displayAnimated) { + CABasicAnimation* rotationAnimation; + rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; + rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI]; + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + rotationAnimation.duration = 0.1; + rotationAnimation.repeatCount = 0;//������������������������������������ + rotationAnimation.cumulative = NO; + rotationAnimation.removedOnCompletion = NO; + rotationAnimation.fillMode = kCAFillModeForwards; + [self.textLayer addAnimation:rotationAnimation forKey:@"Rotation"]; + } +} + +@end diff --git a/PNChartdemo/PNChart/PNBarChart.h b/PNChartdemo/PNChart/PNBarChart.h new file mode 100755 index 0000000..e628c08 --- /dev/null +++ b/PNChartdemo/PNChart/PNBarChart.h @@ -0,0 +1,123 @@ +// +// PNBarChart.h +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "PNGenericChart.h" +#import "PNChartDelegate.h" +#import "PNBar.h" + +#define kXLabelMargin 15 +#define kYLabelMargin 15 +#define kYLabelHeight 11 +#define kXLabelHeight 20 + +typedef NSString *(^PNYLabelFormatter)(CGFloat yLabelValue); + +@interface PNBarChart : PNGenericChart + +/** + * Draws the chart in an animated fashion. + */ +- (void)strokeChart; + +@property (nonatomic) NSArray *xLabels; +@property (nonatomic) NSArray *yLabels; +@property (nonatomic) NSArray *yValues; + +@property (nonatomic) NSMutableArray * bars; + +@property (nonatomic) CGFloat xLabelWidth; +@property (nonatomic) float yValueMax; +@property (nonatomic) UIColor *strokeColor; +@property (nonatomic) NSArray *strokeColors; + + +/** Update Values. */ +- (void)updateChartData:(NSArray *)data; + +/** Changes chart margin. */ +@property (nonatomic) CGFloat yChartLabelWidth; + +/** Formats the ylabel text. */ +@property (copy) PNYLabelFormatter yLabelFormatter; + +/** Prefix to y label values, none if unset. */ +@property (nonatomic) NSString *yLabelPrefix; + +/** Suffix to y label values, none if unset. */ +@property (nonatomic) NSString *yLabelSuffix; + +@property (nonatomic) CGFloat chartMarginLeft; +@property (nonatomic) CGFloat chartMarginRight; +@property (nonatomic) CGFloat chartMarginTop; +@property (nonatomic) CGFloat chartMarginBottom; + +/** Controls whether labels should be displayed. */ +@property (nonatomic) BOOL showLabel; + +/** Controls whether the chart border line should be displayed. */ +@property (nonatomic) BOOL showChartBorder; + +@property (nonatomic) UIColor *chartBorderColor; + +/** Controls whether the chart Horizontal separator should be displayed. */ +@property (nonatomic, assign) BOOL showLevelLine; + +/** Chart bottom border, co-linear with the x-axis. */ +@property (nonatomic) CAShapeLayer * chartBottomLine; + +/** Chart bottom border, level separator-linear with the x-axis. */ +@property (nonatomic) CAShapeLayer * chartLevelLine; + +/** Chart left border, co-linear with the y-axis. */ +@property (nonatomic) CAShapeLayer * chartLeftLine; + +/** Corner radius for all bars in the chart. */ +@property (nonatomic) CGFloat barRadius; + +/** Width of all bars in the chart. */ +@property (nonatomic) CGFloat barWidth; + +@property (nonatomic) CGFloat labelMarginTop; + +/** Background color of all bars in the chart. */ +@property (nonatomic) UIColor * barBackgroundColor; + +/** Text color for all bars in the chart. */ +@property (nonatomic) UIColor * labelTextColor; + +/** Font for all bars in the chart. */ +@property (nonatomic) UIFont * labelFont; + +/** How many labels on the x-axis to skip in between displaying labels. */ +@property (nonatomic) NSInteger xLabelSkip; + +/** How many labels on the y-axis to skip in between displaying labels. */ +@property (nonatomic) NSInteger yLabelSum; + +/** The maximum for the range of values to display on the y-axis. */ +@property (nonatomic) CGFloat yMaxValue; + +/** The minimum for the range of values to display on the y-axis. */ +@property (nonatomic) CGFloat yMinValue; + +/** Controls whether each bar should have a gradient fill. */ +@property (nonatomic) UIColor *barColorGradientStart; + +/** Controls whether text for x-axis be straight or rotate 45 degree. */ +@property (nonatomic) BOOL rotateForXAxisText; + +@property (nonatomic, weak) id<PNChartDelegate> delegate; + +/**whether show gradient bar*/ +@property (nonatomic, assign) BOOL isGradientShow; + +/** whether show numbers*/ +@property (nonatomic, assign) BOOL isShowNumbers; + +@end diff --git a/PNChartdemo/PNChart/PNBarChart.m b/PNChartdemo/PNChart/PNBarChart.m new file mode 100755 index 0000000..50e035c --- /dev/null +++ b/PNChartdemo/PNChart/PNBarChart.m @@ -0,0 +1,459 @@ +// +// PNBarChart.m +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNBarChart.h" +#import "PNColor.h" +#import "PNChartLabel.h" + +@interface PNBarChart () { + NSMutableArray *_xChartLabels; + NSMutableArray *_yChartLabels; +} + +- (UIColor *)barColorAtIndex:(NSUInteger)index; + +@end + +@implementation PNBarChart + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + + if (self) { + [self setupDefaultValues]; + } + return self; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + [self setupDefaultValues]; + } + + return self; +} + +- (void)setupDefaultValues +{ + [super setupDefaultValues]; + self.backgroundColor = [UIColor whiteColor]; + self.clipsToBounds = YES; + _showLabel = YES; + _barBackgroundColor = PNLightGrey; + _labelTextColor = [UIColor grayColor]; + _labelFont = [UIFont systemFontOfSize:11.0f]; + _xChartLabels = [NSMutableArray array]; + _yChartLabels = [NSMutableArray array]; + _bars = [NSMutableArray array]; + _xLabelSkip = 1; + _yLabelSum = 4; + _labelMarginTop = 2; + _chartMarginLeft = 25.0; + _chartMarginRight = 25.0; + _chartMarginTop = 25.0; + _chartMarginBottom = 25.0; + _barRadius = 2.0; + _showChartBorder = NO; + _chartBorderColor = PNLightGrey; + _showLevelLine = NO; + _yChartLabelWidth = 18; + _rotateForXAxisText = false; + _isGradientShow = YES; + _isShowNumbers = YES; + _yLabelPrefix = @""; + _yLabelSuffix = @""; + _yLabelFormatter = ^(CGFloat yValue){ + return [NSString stringWithFormat:@"%1.f",yValue]; + }; +} + +- (void)setYValues:(NSArray *)yValues +{ + _yValues = yValues; + //make the _yLabelSum value dependant of the distinct values of yValues to avoid duplicates on yAxis + + if (_showLabel) { + [self __addYCoordinateLabelsValues]; + } else { + [self processYMaxValue]; + } +} + +- (void)processYMaxValue { + NSArray *yAxisValues = _yLabels ? _yLabels : _yValues; + _yLabelSum = _yLabels ? _yLabels.count - 1 :_yLabelSum; + if (_yMaxValue) { + _yValueMax = _yMaxValue; + } else { + [self getYValueMax:yAxisValues]; + } + + if (_yLabelSum==4) { + _yLabelSum = yAxisValues.count; + (_yLabelSum % 2 == 0) ? _yLabelSum : _yLabelSum++; + } +} + +#pragma mark - Private Method +#pragma mark - Add Y Label +- (void)__addYCoordinateLabelsValues{ + + [self viewCleanupForCollection:_yChartLabels]; + + [self processYMaxValue]; + + float sectionHeight = (self.frame.size.height - _chartMarginTop - _chartMarginBottom - kXLabelHeight) / _yLabelSum; + + for (int i = 0; i <= _yLabelSum; i++) { + NSString *labelText; + if (_yLabels) { + float yAsixValue = [_yLabels[_yLabels.count - i - 1] floatValue]; + labelText= _yLabelFormatter(yAsixValue); + } else { + labelText = _yLabelFormatter((float)_yValueMax * ( (_yLabelSum - i) / (float)_yLabelSum )); + } + + PNChartLabel *label = [[PNChartLabel alloc] initWithFrame:CGRectZero]; + label.font = _labelFont; + label.textColor = _labelTextColor; + [label setTextAlignment:NSTextAlignmentRight]; + label.text = [NSString stringWithFormat:@"%@%@%@", _yLabelPrefix, labelText, _yLabelSuffix]; + + [self addSubview:label]; + + label.frame = (CGRect){0, sectionHeight * i + _chartMarginTop - kYLabelHeight/2.0 + kXLabelHeight + _labelMarginTop, _yChartLabelWidth, kYLabelHeight}; + + [_yChartLabels addObject:label]; + } +} + +-(void)updateChartData:(NSArray *)data{ + self.yValues = data; + [self updateBar]; +} + +- (void)getYValueMax:(NSArray *)yLabels +{ + CGFloat max = [[yLabels valueForKeyPath:@"@max.floatValue"] floatValue]; + + //ensure max is even + _yValueMax = max ; + + if (_yValueMax == 0) { + _yValueMax = _yMinValue; + } +} + +- (void)setXLabels:(NSArray *)xLabels +{ + _xLabels = xLabels; + + if (_xChartLabels) { + [self viewCleanupForCollection:_xChartLabels]; + }else{ + _xChartLabels = [NSMutableArray new]; + } + + _xLabelWidth = (self.frame.size.width - _chartMarginLeft - _chartMarginRight) / [xLabels count]; + + if (_showLabel) { + int labelAddCount = 0; + for (int index = 0; index < _xLabels.count; index++) { + labelAddCount += 1; + + if (labelAddCount == _xLabelSkip) { + NSString *labelText = [_xLabels[index] description]; + PNChartLabel * label = [[PNChartLabel alloc] initWithFrame:CGRectMake(0, 0, _xLabelWidth, kXLabelHeight)]; + label.font = _labelFont; + label.textColor = _labelTextColor; + [label setTextAlignment:NSTextAlignmentCenter]; + label.text = labelText; + //[label sizeToFit]; + CGFloat labelXPosition; + if (_rotateForXAxisText){ + label.transform = CGAffineTransformMakeRotation(M_PI / 4); + labelXPosition = (index * _xLabelWidth + _chartMarginLeft + _xLabelWidth /1.5); + } + else{ + labelXPosition = (index * _xLabelWidth + _chartMarginLeft + _xLabelWidth /2.0 ); + } + label.center = CGPointMake(labelXPosition, + self.frame.size.height - _chartMarginTop + label.frame.size.height /2.0 + _labelMarginTop); + labelAddCount = 0; + + [_xChartLabels addObject:label]; + [self addSubview:label]; + } + } + } +} + + +- (void)setStrokeColor:(UIColor *)strokeColor +{ + _strokeColor = strokeColor; +} + +- (void)updateBar +{ + + //Add bars + CGFloat chartCavanHeight = self.frame.size.height - _chartMarginTop - _chartMarginBottom - kXLabelHeight; + NSInteger index = 0; + + for (NSNumber *valueString in _yValues) { + + PNBar *bar; + + if (_bars.count == _yValues.count) { + bar = [_bars objectAtIndex:index]; + }else{ + CGFloat barWidth; + CGFloat barXPosition; + + if (_barWidth) { + barWidth = _barWidth; + barXPosition = index * _xLabelWidth + _chartMarginLeft + _xLabelWidth /2.0 - _barWidth /2.0; + }else{ + barXPosition = index * _xLabelWidth + _chartMarginLeft + _xLabelWidth * 0.25; + if (_showLabel) { + barWidth = _xLabelWidth * 0.5; + + } + else { + barWidth = _xLabelWidth * 0.6; + + } + } + + bar = [[PNBar alloc] initWithFrame:CGRectMake(barXPosition, //Bar X position + self.frame.size.height - chartCavanHeight - kXLabelHeight - _chartMarginBottom + _chartMarginTop , //Bar Y position + barWidth, // Bar witdh + self.showLevelLine ? chartCavanHeight/2.0:chartCavanHeight)]; //Bar height + + //Change Bar Radius + bar.barRadius = _barRadius; + + //Set Bar Animation + bar.displayAnimated = self.displayAnimated; + + //Change Bar Background color + bar.backgroundColor = _barBackgroundColor; + //Bar StrokColor First + if (self.strokeColor) { + bar.barColor = self.strokeColor; + }else{ + bar.barColor = [self barColorAtIndex:index]; + } + + if (self.labelTextColor) { + bar.labelTextColor = self.labelTextColor; + } + + // Add gradient + if (self.isGradientShow) { + bar.barColorGradientStart = bar.barColor; + } + + //For Click Index + bar.tag = index; + + [_bars addObject:bar]; + [self addSubview:bar]; + } + + //Height Of Bar + float value = [valueString floatValue]; + float grade =fabsf((float)value / (float)_yValueMax); + + if (isnan(grade)) { + grade = 0; + } + bar.maxDivisor = (float)_yValueMax; + bar.grade = grade; + bar.isShowNumber = self.isShowNumbers; + CGRect originalFrame = bar.frame; + NSString *currentNumber = [NSString stringWithFormat:@"%f",value]; + + if ([[currentNumber substringToIndex:1] isEqualToString:@"-"] && self.showLevelLine) { + CGAffineTransform transform =CGAffineTransformMakeRotation(M_PI); + [bar setTransform:transform]; + originalFrame.origin.y = bar.frame.origin.y + bar.frame.size.height; + bar.frame = originalFrame; + bar.isNegative = YES; + + } + index += 1; + } +} + +- (void)strokeChart +{ + //Add Labels + + [self viewCleanupForCollection:_bars]; + + + //Update Bar + + [self updateBar]; + + //Add chart border lines + + if (_showChartBorder) { + _chartBottomLine = [CAShapeLayer layer]; + _chartBottomLine.lineCap = kCALineCapButt; + _chartBottomLine.fillColor = [[UIColor whiteColor] CGColor]; + _chartBottomLine.lineWidth = 1.0; + _chartBottomLine.strokeEnd = 0.0; + + UIBezierPath *progressline = [UIBezierPath bezierPath]; + + [progressline moveToPoint:CGPointMake(_chartMarginLeft, self.frame.size.height - kXLabelHeight - _chartMarginBottom + _chartMarginTop)]; + [progressline addLineToPoint:CGPointMake(self.frame.size.width - _chartMarginRight, self.frame.size.height - kXLabelHeight - _chartMarginBottom + _chartMarginTop)]; + + [progressline setLineWidth:1.0]; + [progressline setLineCapStyle:kCGLineCapSquare]; + _chartBottomLine.path = progressline.CGPath; + _chartBottomLine.strokeColor = [_chartBorderColor CGColor];; + _chartBottomLine.strokeEnd = 1.0; + + [self.layer addSublayer:_chartBottomLine]; + + //Add left Chart Line + + _chartLeftLine = [CAShapeLayer layer]; + _chartLeftLine.lineCap = kCALineCapButt; + _chartLeftLine.fillColor = [[UIColor whiteColor] CGColor]; + _chartLeftLine.lineWidth = 1.0; + _chartLeftLine.strokeEnd = 0.0; + + UIBezierPath *progressLeftline = [UIBezierPath bezierPath]; + + [progressLeftline moveToPoint:CGPointMake(_chartMarginLeft, self.frame.size.height - kXLabelHeight - _chartMarginBottom + _chartMarginTop)]; + [progressLeftline addLineToPoint:CGPointMake(_chartMarginLeft, _chartMarginTop)]; + + [progressLeftline setLineWidth:1.0]; + [progressLeftline setLineCapStyle:kCGLineCapSquare]; + _chartLeftLine.path = progressLeftline.CGPath; + _chartLeftLine.strokeColor = [_chartBorderColor CGColor]; + _chartLeftLine.strokeEnd = 1.0; + + [self addBorderAnimationIfNeeded]; + [self.layer addSublayer:_chartLeftLine]; + } + + // Add Level Separator Line + if (_showLevelLine) { + _chartLevelLine = [CAShapeLayer layer]; + _chartLevelLine.lineCap = kCALineCapButt; + _chartLevelLine.fillColor = [[UIColor whiteColor] CGColor]; + _chartLevelLine.lineWidth = 1.0; + _chartLevelLine.strokeEnd = 0.0; + + UIBezierPath *progressline = [UIBezierPath bezierPath]; + + [progressline moveToPoint:CGPointMake(_chartMarginLeft, (self.frame.size.height - kXLabelHeight )/2.0)]; + [progressline addLineToPoint:CGPointMake(self.frame.size.width - _chartMarginLeft - _chartMarginRight, (self.frame.size.height - kXLabelHeight )/2.0)]; + + [progressline setLineWidth:1.0]; + [progressline setLineCapStyle:kCGLineCapSquare]; + _chartLevelLine.path = progressline.CGPath; + + _chartLevelLine.strokeColor = PNLightGrey.CGColor; + + [self addSeparatorAnimationIfNeeded]; + _chartLevelLine.strokeEnd = 1.0; + + [self.layer addSublayer:_chartLevelLine]; + } else { + if (_chartLevelLine) { + [_chartLevelLine removeFromSuperlayer]; + _chartLevelLine = nil; + } + } +} + +- (void)addBorderAnimationIfNeeded +{ + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathAnimation.duration = 0.5; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @0.0f; + pathAnimation.toValue = @1.0f; + [_chartBottomLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + + CABasicAnimation *pathLeftAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathLeftAnimation.duration = 0.5; + pathLeftAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathLeftAnimation.fromValue = @0.0f; + pathLeftAnimation.toValue = @1.0f; + [_chartLeftLine addAnimation:pathLeftAnimation forKey:@"strokeEndAnimation"]; + } +} + +- (void)addSeparatorAnimationIfNeeded +{ + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathAnimation.duration = 0.5; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @0.0f; + pathAnimation.toValue = @1.0f; + [_chartLevelLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + } +} + +- (void)viewCleanupForCollection:(NSMutableArray *)array +{ + if (array.count) { + [array makeObjectsPerformSelector:@selector(removeFromSuperview)]; + [array removeAllObjects]; + } +} + + +#pragma mark - Class extension methods + +- (UIColor *)barColorAtIndex:(NSUInteger)index +{ + if ([self.strokeColors count] == [self.yValues count]) { + return self.strokeColors[index]; + } + else { + return self.strokeColor; + } +} + +#pragma mark - Touch detection + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [self touchPoint:touches withEvent:event]; + [super touchesBegan:touches withEvent:event]; +} + +- (void)touchPoint:(NSSet *)touches withEvent:(UIEvent *)event +{ + //Get the point user touched + UITouch *touch = [touches anyObject]; + CGPoint touchPoint = [touch locationInView:self]; + UIView *subview = [self hitTest:touchPoint withEvent:nil]; + + if ([subview isKindOfClass:[PNBar class]] && [self.delegate respondsToSelector:@selector(userClickedOnBarAtIndex:)]) { + [self.delegate userClickedOnBarAtIndex:subview.tag]; + } +} + + +@end diff --git a/PNChartdemo/PNChart/PNChart.h b/PNChartdemo/PNChart/PNChart.h new file mode 100755 index 0000000..0835d39 --- /dev/null +++ b/PNChartdemo/PNChart/PNChart.h @@ -0,0 +1,22 @@ +// +// PNChart.h +// Version 0.1 +// PNChart +// +// Created by kevin on 10/3/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "PNChart.h" +#import "PNColor.h" +#import "PNLineChart.h" +#import "PNLineChartData.h" +#import "PNLineChartDataItem.h" +#import "PNBarChart.h" +#import "PNCircleChart.h" +#import "PNChartDelegate.h" +#import "PNPieChart.h" +#import "PNScatterChart.h" +#import "PNRadarChart.h" +#import "PNRadarChartDataItem.h" diff --git a/PNChartdemo/PNChart/PNChartDelegate.h b/PNChartdemo/PNChart/PNChartDelegate.h new file mode 100755 index 0000000..6d49f7c --- /dev/null +++ b/PNChartdemo/PNChart/PNChartDelegate.h @@ -0,0 +1,33 @@ +// +// PNChartDelegate.h +// PNChartDemo +// +// Created by kevinzhow on 13-12-11. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> + +@protocol PNChartDelegate <NSObject> +@optional +/** + * Callback method that gets invoked when the user taps on the chart line. + */ +- (void)userClickedOnLinePoint:(CGPoint)point lineIndex:(NSInteger)lineIndex; + +/** + * Callback method that gets invoked when the user taps on a chart line key point. + */ +- (void)userClickedOnLineKeyPoint:(CGPoint)point + lineIndex:(NSInteger)lineIndex + pointIndex:(NSInteger)pointIndex; + +/** + * Callback method that gets invoked when the user taps on a chart bar. + */ +- (void)userClickedOnBarAtIndex:(NSInteger)barIndex; + + +- (void)userClickedOnPieIndexItem:(NSInteger)pieIndex; +- (void)didUnselectPieItem; +@end diff --git a/PNChartdemo/PNChart/PNChartLabel.h b/PNChartdemo/PNChart/PNChartLabel.h new file mode 100755 index 0000000..9ba6afa --- /dev/null +++ b/PNChartdemo/PNChart/PNChartLabel.h @@ -0,0 +1,13 @@ +// +// PNChartLabel.h +// PNChart +// +// Created by kevin on 10/3/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> + +@interface PNChartLabel : UILabel + +@end diff --git a/PNChartdemo/PNChart/PNChartLabel.m b/PNChartdemo/PNChart/PNChartLabel.m new file mode 100755 index 0000000..b0980d1 --- /dev/null +++ b/PNChartdemo/PNChart/PNChartLabel.m @@ -0,0 +1,32 @@ +// +// PNChartLabel.m +// PNChart +// +// Created by kevin on 10/3/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNChartLabel.h" + +@implementation PNChartLabel + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + self.font = [UIFont boldSystemFontOfSize:11.0f]; + self.backgroundColor = [UIColor clearColor]; + self.textAlignment = NSTextAlignmentCenter; + self.userInteractionEnabled = YES; + self.adjustsFontSizeToFitWidth = YES; + self.numberOfLines = 0; + /* if you want to see ... in large labels un-comment this line + self.minimumScaleFactor = 0.8; + */ + } + + return self; +} + +@end diff --git a/PNChartdemo/PNChart/PNCircleChart.h b/PNChartdemo/PNChart/PNCircleChart.h new file mode 100755 index 0000000..f9fc06d --- /dev/null +++ b/PNChartdemo/PNChart/PNCircleChart.h @@ -0,0 +1,74 @@ +// +// PNCircleChart.h +// PNChartDemo +// +// Created by kevinzhow on 13-11-30. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "PNColor.h" +#import "UICountingLabel.h" + +typedef NS_ENUM (NSUInteger, PNChartFormatType) { + PNChartFormatTypePercent, + PNChartFormatTypeDollar, + PNChartFormatTypeNone, + PNChartFormatTypeDecimal, + PNChartFormatTypeDecimalTwoPlaces, +}; + +#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI) + +@interface PNCircleChart : UIView + +- (void)strokeChart; +- (void)growChartByAmount:(NSNumber *)growAmount; +- (void)updateChartByCurrent:(NSNumber *)current; +- (void)updateChartByCurrent:(NSNumber *)current byTotal:(NSNumber *)total; +- (id)initWithFrame:(CGRect)frame + total:(NSNumber *)total + current:(NSNumber *)current + clockwise:(BOOL)clockwise; + +- (id)initWithFrame:(CGRect)frame + total:(NSNumber *)total + current:(NSNumber *)current + clockwise:(BOOL)clockwise + shadow:(BOOL)hasBackgroundShadow + shadowColor:(UIColor *)backgroundShadowColor; + +- (id)initWithFrame:(CGRect)frame + total:(NSNumber *)total + current:(NSNumber *)current + clockwise:(BOOL)clockwise + shadow:(BOOL)hasBackgroundShadow + shadowColor:(UIColor *)backgroundShadowColor +displayCountingLabel:(BOOL)displayCountingLabel; + +- (id)initWithFrame:(CGRect)frame + total:(NSNumber *)total + current:(NSNumber *)current + clockwise:(BOOL)clockwise + shadow:(BOOL)hasBackgroundShadow + shadowColor:(UIColor *)backgroundShadowColor +displayCountingLabel:(BOOL)displayCountingLabel + overrideLineWidth:(NSNumber *)overrideLineWidth; + +@property (strong, nonatomic) UICountingLabel *countingLabel; +@property (nonatomic) UIColor *strokeColor; +@property (nonatomic) UIColor *strokeColorGradientStart; +@property (nonatomic) NSNumber *total; +@property (nonatomic) NSNumber *current; +@property (nonatomic) NSNumber *lineWidth; +@property (nonatomic) NSTimeInterval duration; +@property (nonatomic) PNChartFormatType chartType; + +@property (nonatomic) CAShapeLayer *circle; +@property (nonatomic) CAShapeLayer *gradientMask; +@property (nonatomic) CAShapeLayer *circleBackground; + +@property (nonatomic) BOOL displayCountingLabel; +@property (nonatomic) BOOL displayAnimated; + +@end diff --git a/PNChartdemo/PNChart/PNCircleChart.m b/PNChartdemo/PNChart/PNCircleChart.m new file mode 100755 index 0000000..19f70e5 --- /dev/null +++ b/PNChartdemo/PNChart/PNCircleChart.m @@ -0,0 +1,267 @@ +// +// PNCircleChart.m +// PNChartDemo +// +// Created by kevinzhow on 13-11-30. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNCircleChart.h" + +@interface PNCircleChart () +@end + +@implementation PNCircleChart + +- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise { + + return [self initWithFrame:frame + total:total + current:current + clockwise:clockwise + shadow:NO + shadowColor:[UIColor clearColor] + displayCountingLabel:YES + overrideLineWidth:@8.0f]; + +} + +- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise shadow:(BOOL)hasBackgroundShadow shadowColor:(UIColor *)backgroundShadowColor { + + return [self initWithFrame:frame + total:total + current:current + clockwise:clockwise + shadow:shadow + shadowColor:backgroundShadowColor + displayCountingLabel:YES + overrideLineWidth:@8.0f]; + +} + +- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise shadow:(BOOL)hasBackgroundShadow shadowColor:(UIColor *)backgroundShadowColor displayCountingLabel:(BOOL)displayCountingLabel { + + return [self initWithFrame:frame + total:total + current:current + clockwise:clockwise + shadow:shadow + shadowColor:backgroundShadowColor + displayCountingLabel:displayCountingLabel + overrideLineWidth:@8.0f]; + +} + +- (id)initWithFrame:(CGRect)frame + total:(NSNumber *)total + current:(NSNumber *)current + clockwise:(BOOL)clockwise + shadow:(BOOL)hasBackgroundShadow + shadowColor:(UIColor *)backgroundShadowColor +displayCountingLabel:(BOOL)displayCountingLabel + overrideLineWidth:(NSNumber *)overrideLineWidth +{ + self = [super initWithFrame:frame]; + + if (self) { + _total = total; + _current = current; + _strokeColor = PNFreshGreen; + _duration = 1.0; + _chartType = PNChartFormatTypePercent; + _displayAnimated = YES; + + _displayCountingLabel = displayCountingLabel; + + CGFloat startAngle = clockwise ? -90.0f : 270.0f; + CGFloat endAngle = clockwise ? -90.01f : 270.01f; + + _lineWidth = overrideLineWidth; + + UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width/2.0f, self.frame.size.height/2.0f) + radius:(self.frame.size.height * 0.5) - ([_lineWidth floatValue]/2.0f) + startAngle:DEGREES_TO_RADIANS(startAngle) + endAngle:DEGREES_TO_RADIANS(endAngle) + clockwise:clockwise]; + + _circle = [CAShapeLayer layer]; + _circle.path = circlePath.CGPath; + _circle.lineCap = kCALineCapRound; + _circle.fillColor = [UIColor clearColor].CGColor; + _circle.lineWidth = [_lineWidth floatValue]; + _circle.zPosition = 1; + + _circleBackground = [CAShapeLayer layer]; + _circleBackground.path = circlePath.CGPath; + _circleBackground.lineCap = kCALineCapRound; + _circleBackground.fillColor = [UIColor clearColor].CGColor; + _circleBackground.lineWidth = [_lineWidth floatValue]; + _circleBackground.strokeColor = (hasBackgroundShadow ? backgroundShadowColor.CGColor : [UIColor clearColor].CGColor); + _circleBackground.strokeEnd = 1.0; + _circleBackground.zPosition = -1; + + [self.layer addSublayer:_circle]; + [self.layer addSublayer:_circleBackground]; + + _countingLabel = [[UICountingLabel alloc] initWithFrame:CGRectMake(0, 0, 100.0, 50.0)]; + [_countingLabel setTextAlignment:NSTextAlignmentCenter]; + [_countingLabel setFont:[UIFont boldSystemFontOfSize:16.0f]]; + [_countingLabel setTextColor:[UIColor grayColor]]; + [_countingLabel setBackgroundColor:[UIColor clearColor]]; + [_countingLabel setCenter:CGPointMake(self.frame.size.width/2.0f, self.frame.size.height/2.0f)]; + _countingLabel.method = UILabelCountingMethodEaseInOut; + if (_displayCountingLabel) { + [self addSubview:_countingLabel]; + } + } + + return self; +} + + +- (void)strokeChart +{ + // Add counting label + + if (_displayCountingLabel) { + NSString *format; + switch (self.chartType) { + case PNChartFormatTypePercent: + format = @"%d%%"; + break; + case PNChartFormatTypeDollar: + format = @"$%d"; + break; + case PNChartFormatTypeDecimal: + format = @"%.1f"; + break; + case PNChartFormatTypeDecimalTwoPlaces: + format = @"%.2f"; + break; + case PNChartFormatTypeNone: + default: + format = @"%d"; + break; + } + self.countingLabel.format = format; + [self addSubview:self.countingLabel]; + } + + + // Add circle params + + _circle.lineWidth = [_lineWidth floatValue]; + _circleBackground.lineWidth = [_lineWidth floatValue]; + _circleBackground.strokeEnd = 1.0; + _circle.strokeColor = _strokeColor.CGColor; + _circle.strokeEnd = [_current floatValue] / [_total floatValue]; + + // Check if user wants to add a gradient from the start color to the bar color + if (_strokeColorGradientStart) { + + // Add gradient + self.gradientMask = [CAShapeLayer layer]; + self.gradientMask.fillColor = [[UIColor clearColor] CGColor]; + self.gradientMask.strokeColor = [[UIColor blackColor] CGColor]; + self.gradientMask.lineWidth = _circle.lineWidth; + self.gradientMask.lineCap = kCALineCapRound; + CGRect gradientFrame = CGRectMake(0, 0, 2*self.bounds.size.width, 2*self.bounds.size.height); + self.gradientMask.frame = gradientFrame; + self.gradientMask.path = _circle.path; + + CAGradientLayer *gradientLayer = [CAGradientLayer layer]; + gradientLayer.startPoint = CGPointMake(0.5,1.0); + gradientLayer.endPoint = CGPointMake(0.5,0.0); + gradientLayer.frame = gradientFrame; + UIColor *endColor = (_strokeColor ? _strokeColor : [UIColor greenColor]); + NSArray *colors = @[ + (id)endColor.CGColor, + (id)_strokeColorGradientStart.CGColor + ]; + gradientLayer.colors = colors; + + [gradientLayer setMask:self.gradientMask]; + + [_circle addSublayer:gradientLayer]; + + self.gradientMask.strokeEnd = [_current floatValue] / [_total floatValue]; + } + + [self addAnimationIfNeeded]; +} + + + +- (void)growChartByAmount:(NSNumber *)growAmount +{ + NSNumber *updatedValue = [NSNumber numberWithFloat:[_current floatValue] + [growAmount floatValue]]; + + // Add animation + [self updateChartByCurrent:updatedValue]; +} + + +-(void)updateChartByCurrent:(NSNumber *)current{ + + [self updateChartByCurrent:current + byTotal:_total]; + +} + +-(void)updateChartByCurrent:(NSNumber *)current byTotal:(NSNumber *)total { + double totalPercentageValue = [current floatValue]/([total floatValue]/100.0); + + if (_strokeColorGradientStart) { + self.gradientMask.strokeEnd = _circle.strokeEnd; + } + + // Add animation + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathAnimation.duration = self.duration; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @([_current floatValue] / [_total floatValue]); + pathAnimation.toValue = @([current floatValue] / [total floatValue]); + + if (_strokeColorGradientStart) { + [self.gradientMask addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + } + [_circle addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + + if (_displayCountingLabel) { + [self.countingLabel countFrom:fmin([_current floatValue], [_total floatValue]) to:totalPercentageValue withDuration:self.duration]; + } + + } + else if (_displayCountingLabel) { + [self.countingLabel countFrom:totalPercentageValue to:totalPercentageValue withDuration:self.duration]; + } + + _circle.strokeEnd = [current floatValue] / [total floatValue]; + _current = current; + _total = total; +} + +- (void)addAnimationIfNeeded +{ + double percentageValue = [_current floatValue]/([_total floatValue]/100.0); + + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathAnimation.duration = self.duration; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @(0.0f); + pathAnimation.toValue = @([_current floatValue] / [_total floatValue]); + [_circle addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + [_countingLabel countFrom:0 to:percentageValue withDuration:self.duration]; + + if (self.gradientMask && _strokeColorGradientStart) { + [self.gradientMask addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + } + } + else { + [_countingLabel countFrom:percentageValue to:percentageValue withDuration:self.duration]; + } +} + +@end diff --git a/PNChartdemo/PNChart/PNColor.h b/PNChartdemo/PNChart/PNColor.h new file mode 100755 index 0000000..cfd3ff8 --- /dev/null +++ b/PNChartdemo/PNChart/PNColor.h @@ -0,0 +1,53 @@ +// +// PNColor.h +// PNChart +// +// Created by kevin on 13-6-8. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +/* + * System Versioning Preprocessor Macros + */ + +#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width) + +#define PNGrey [UIColor colorWithRed:246.0 / 255.0 green:246.0 / 255.0 blue:246.0 / 255.0 alpha:1.0f] +#define PNLightBlue [UIColor colorWithRed:94.0 / 255.0 green:147.0 / 255.0 blue:196.0 / 255.0 alpha:1.0f] +#define PNGreen [UIColor colorWithRed:77.0 / 255.0 green:186.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] +#define PNTitleColor [UIColor colorWithRed:0.0 / 255.0 green:189.0 / 255.0 blue:113.0 / 255.0 alpha:1.0f] +#define PNButtonGrey [UIColor colorWithRed:141.0 / 255.0 green:141.0 / 255.0 blue:141.0 / 255.0 alpha:1.0f] +#define PNLightGreen [UIColor colorWithRed:77.0 / 255.0 green:216.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] +#define PNFreshGreen [UIColor colorWithRed:77.0 / 255.0 green:196.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] +#define PNDeepGreen [UIColor colorWithRed:77.0 / 255.0 green:176.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] +#define PNRed [UIColor colorWithRed:245.0 / 255.0 green:94.0 / 255.0 blue:78.0 / 255.0 alpha:1.0f] +#define PNMauve [UIColor colorWithRed:88.0 / 255.0 green:75.0 / 255.0 blue:103.0 / 255.0 alpha:1.0f] +#define PNBrown [UIColor colorWithRed:119.0 / 255.0 green:107.0 / 255.0 blue:95.0 / 255.0 alpha:1.0f] +#define PNBlue [UIColor colorWithRed:82.0 / 255.0 green:116.0 / 255.0 blue:188.0 / 255.0 alpha:1.0f] +#define PNDarkBlue [UIColor colorWithRed:121.0 / 255.0 green:134.0 / 255.0 blue:142.0 / 255.0 alpha:1.0f] +#define PNYellow [UIColor colorWithRed:242.0 / 255.0 green:197.0 / 255.0 blue:117.0 / 255.0 alpha:1.0f] +#define PNWhite [UIColor colorWithRed:255.0 / 255.0 green:255.0 / 255.0 blue:255.0 / 255.0 alpha:1.0f] +#define PNDeepGrey [UIColor colorWithRed:99.0 / 255.0 green:99.0 / 255.0 blue:99.0 / 255.0 alpha:1.0f] +#define PNPinkGrey [UIColor colorWithRed:200.0 / 255.0 green:193.0 / 255.0 blue:193.0 / 255.0 alpha:1.0f] +#define PNHealYellow [UIColor colorWithRed:245.0 / 255.0 green:242.0 / 255.0 blue:238.0 / 255.0 alpha:1.0f] +#define PNLightGrey [UIColor colorWithRed:225.0 / 255.0 green:225.0 / 255.0 blue:225.0 / 255.0 alpha:1.0f] +#define PNCleanGrey [UIColor colorWithRed:251.0 / 255.0 green:251.0 / 255.0 blue:251.0 / 255.0 alpha:1.0f] +#define PNLightYellow [UIColor colorWithRed:241.0 / 255.0 green:240.0 / 255.0 blue:240.0 / 255.0 alpha:1.0f] +#define PNDarkYellow [UIColor colorWithRed:152.0 / 255.0 green:150.0 / 255.0 blue:159.0 / 255.0 alpha:1.0f] +#define PNPinkDark [UIColor colorWithRed:170.0 / 255.0 green:165.0 / 255.0 blue:165.0 / 255.0 alpha:1.0f] +#define PNCloudWhite [UIColor colorWithRed:244.0 / 255.0 green:244.0 / 255.0 blue:244.0 / 255.0 alpha:1.0f] +#define PNBlack [UIColor colorWithRed:45.0 / 255.0 green:45.0 / 255.0 blue:45.0 / 255.0 alpha:1.0f] +#define PNStarYellow [UIColor colorWithRed:252.0 / 255.0 green:223.0 / 255.0 blue:101.0 / 255.0 alpha:1.0f] +#define PNTwitterColor [UIColor colorWithRed:0.0 / 255.0 green:171.0 / 255.0 blue:243.0 / 255.0 alpha:1.0] +#define PNWeiboColor [UIColor colorWithRed:250.0 / 255.0 green:0.0 / 255.0 blue:33.0 / 255.0 alpha:1.0] +#define PNiOSGreenColor [UIColor colorWithRed:98.0 / 255.0 green:247.0 / 255.0 blue:77.0 / 255.0 alpha:1.0] + + +@interface PNColor : NSObject + +- (UIImage *)imageFromColor:(UIColor *)color; + +@end diff --git a/PNChartdemo/PNChart/PNColor.m b/PNChartdemo/PNChart/PNColor.m new file mode 100755 index 0000000..2ebc8c0 --- /dev/null +++ b/PNChartdemo/PNChart/PNColor.m @@ -0,0 +1,29 @@ +// +// PNColor.m +// PNChart +// +// Created by kevin on 13-6-8. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNColor.h" +#import <UIKit/UIKit.h> + +@implementation PNColor + +- (UIImage *)imageFromColor:(UIColor *)color +{ + CGRect rect = CGRectMake(0, 0, 1, 1); + + UIGraphicsBeginImageContext(rect.size); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetFillColorWithColor(context, [color CGColor]); + CGContextFillRect(context, rect); + UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return img; +} + + +@end diff --git a/PNChartdemo/PNChart/PNGenericChart.h b/PNChartdemo/PNChart/PNGenericChart.h new file mode 100755 index 0000000..829d84a --- /dev/null +++ b/PNChartdemo/PNChart/PNGenericChart.h @@ -0,0 +1,48 @@ +// +// PNGenericChart.h +// PNChartDemo +// +// Created by Andi Palo on 26/02/15. +// Copyright (c) 2015 kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> + +typedef NS_ENUM(NSUInteger, PNLegendPosition) { + PNLegendPositionTop = 0, + PNLegendPositionBottom = 1, + PNLegendPositionLeft = 2, + PNLegendPositionRight = 3 +}; + +typedef NS_ENUM(NSUInteger, PNLegendItemStyle) { + PNLegendItemStyleStacked = 0, + PNLegendItemStyleSerial = 1 +}; + +@interface PNGenericChart : UIView + +@property (assign, nonatomic) BOOL hasLegend; +@property (assign, nonatomic) PNLegendPosition legendPosition; +@property (assign, nonatomic) PNLegendItemStyle legendStyle; + +@property (assign, nonatomic) UIFont *legendFont; +@property (assign, nonatomic) UIColor *legendFontColor; +@property (assign, nonatomic) NSUInteger labelRowsInSerialMode; + +/** Display the chart with or without animation. Default is YES. **/ +@property (nonatomic) BOOL displayAnimated; + +/** + * returns the Legend View, or nil if no chart data is present. + * The origin of the legend frame is 0,0 but you can set it with setFrame:(CGRect) + * + * @param mWidth Maximum width of legend. Height will depend on this and font size + * + * @return UIView of Legend + */ +- (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth; + + +- (void) setupDefaultValues; +@end diff --git a/PNChartdemo/PNChart/PNGenericChart.m b/PNChartdemo/PNChart/PNGenericChart.m new file mode 100755 index 0000000..c54ac37 --- /dev/null +++ b/PNChartdemo/PNChart/PNGenericChart.m @@ -0,0 +1,54 @@ +// +// PNGenericChart.m +// PNChartDemo +// +// Created by Andi Palo on 26/02/15. +// Copyright (c) 2015 kevinzhow. All rights reserved. +// + +#import "PNGenericChart.h" + +@interface PNGenericChart () + + + +@end + +@implementation PNGenericChart + +/* +// Only override drawRect: if you perform custom drawing. +// An empty implementation adversely affects performance during animation. +- (void)drawRect:(CGRect)rect { + // Drawing code +} +*/ + +- (void) setupDefaultValues{ + self.hasLegend = YES; + self.legendPosition = PNLegendPositionBottom; + self.legendStyle = PNLegendItemStyleStacked; + self.labelRowsInSerialMode = 1; + self.displayAnimated = YES; +} + + + +/** + * to be implemented in subclass + */ +- (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth{ + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (void) setLabelRowsInSerialMode:(NSUInteger)num{ + if (self.legendStyle == PNLegendItemStyleSerial) { + _labelRowsInSerialMode = num; + }else{ + _labelRowsInSerialMode = 1; + } +} + + +@end diff --git a/PNChartdemo/PNChart/PNLineChart.h b/PNChartdemo/PNChart/PNLineChart.h new file mode 100755 index 0000000..b30878a --- /dev/null +++ b/PNChartdemo/PNChart/PNLineChart.h @@ -0,0 +1,110 @@ +// +// PNLineChart.h +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import <QuartzCore/QuartzCore.h> +#import "PNChartDelegate.h" +#import "PNGenericChart.h" + +@interface PNLineChart : PNGenericChart + +/** + * Draws the chart in an animated fashion. + */ +- (void)strokeChart; + +@property (nonatomic, weak) id<PNChartDelegate> delegate; + +@property (nonatomic) NSArray *xLabels; +@property (nonatomic) NSArray *yLabels; + +/** + * Array of `LineChartData` objects, one for each line. + */ +@property (nonatomic) NSArray *chartData; + +@property (nonatomic) NSMutableArray *pathPoints; +@property (nonatomic) NSMutableArray *xChartLabels; +@property (nonatomic) NSMutableArray *yChartLabels; + +@property (nonatomic) CGFloat xLabelWidth; +@property (nonatomic) UIFont *xLabelFont; +@property (nonatomic) UIColor *xLabelColor; +@property (nonatomic) CGFloat yValueMax; +@property (nonatomic) CGFloat yFixedValueMax; +@property (nonatomic) CGFloat yFixedValueMin; +@property (nonatomic) CGFloat yValueMin; +@property (nonatomic) NSInteger yLabelNum; +@property (nonatomic) CGFloat yLabelHeight; +@property (nonatomic) UIFont *yLabelFont; +@property (nonatomic) UIColor *yLabelColor; +@property (nonatomic) CGFloat chartCavanHeight; +@property (nonatomic) CGFloat chartCavanWidth; +@property (nonatomic) BOOL showLabel; +@property (nonatomic) BOOL showGenYLabels; +@property (nonatomic) BOOL showYGridLines; +@property (nonatomic) UIColor *yGridLinesColor; +@property (nonatomic) BOOL thousandsSeparator; + +@property (nonatomic) CGFloat chartMarginLeft; +@property (nonatomic) CGFloat chartMarginRight; +@property (nonatomic) CGFloat chartMarginTop; +@property (nonatomic) CGFloat chartMarginBottom; + +/** + * Controls whether to show the coordinate axis. Default is NO. + */ +@property (nonatomic, getter = isShowCoordinateAxis) BOOL showCoordinateAxis; +@property (nonatomic) UIColor *axisColor; +@property (nonatomic) CGFloat axisWidth; + +@property (nonatomic, strong) NSString *xUnit; +@property (nonatomic, strong) NSString *yUnit; + +/** + * String formatter for float values in y-axis labels. If not set, defaults to @"%1.f" + */ +@property (nonatomic, strong) NSString *yLabelFormat; + +/** + * Block formatter for custom string in y-axis labels. If not set, defaults to yLabelFormat + */ +@property (nonatomic, copy) NSString* (^yLabelBlockFormatter)(CGFloat); + + +/** + * Controls whether to curve the line chart or not + */ +@property (nonatomic) BOOL showSmoothLines; + +- (void)setXLabels:(NSArray *)xLabels withWidth:(CGFloat)width; + +/** + * Update Chart Value + */ + +- (void)updateChartData:(NSArray *)data; + + +/** + * returns the Legend View, or nil if no chart data is present. + * The origin of the legend frame is 0,0 but you can set it with setFrame:(CGRect) + * + * @param mWidth Maximum width of legend. Height will depend on this and font size + * + * @return UIView of Legend + */ +- (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth; + + ++ (CGSize)sizeOfString:(NSString *)text withWidth:(float)width font:(UIFont *)font; + ++ (CGPoint)midPointBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2; ++ (CGPoint)controlPointBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2; + +@end diff --git a/PNChartdemo/PNChart/PNLineChart.m b/PNChartdemo/PNChart/PNLineChart.m new file mode 100755 index 0000000..fda132d --- /dev/null +++ b/PNChartdemo/PNChart/PNLineChart.m @@ -0,0 +1,1285 @@ +// +// PNLineChart.m +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNLineChart.h" +#import "PNColor.h" +#import "PNChartLabel.h" +#import "PNLineChartData.h" +#import "PNLineChartDataItem.h" + +@interface PNLineChart () + +@property(nonatomic) NSMutableArray *chartLineArray; // Array[CAShapeLayer] +@property(nonatomic) NSMutableArray *chartPointArray; // Array[CAShapeLayer] save the point layer + +@property(nonatomic) NSMutableArray *chartPath; // Array of line path, one for each line. +@property(nonatomic) NSMutableArray *pointPath; // Array of point path, one for each line +@property(nonatomic) NSMutableArray *endPointsOfPath; // Array of start and end points of each line path, one for each line + +@property(nonatomic) CABasicAnimation *pathAnimation; // will be set to nil if _displayAnimation is NO + +// display grade +@property(nonatomic) NSMutableArray *gradeStringPaths; +@property(nonatomic) NSMutableArray *progressLinePathsColors; //Array of colors when drawing each line.if chartData.rangeColors is set then different colors will be + +@end + +@implementation PNLineChart + +@synthesize pathAnimation = _pathAnimation; + +#pragma mark initialization + +- (id)initWithCoder:(NSCoder *)coder { + self = [super initWithCoder:coder]; + + if (self) { + [self setupDefaultValues]; + } + + return self; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + + if (self) { + [self setupDefaultValues]; + } + + return self; +} + + +#pragma mark instance methods + +- (void)setYLabels { + CGFloat yStep = (_yValueMax - _yValueMin) / _yLabelNum; + CGFloat yStepHeight = _chartCavanHeight / _yLabelNum; + + if (_yChartLabels) { + for (PNChartLabel *label in _yChartLabels) { + [label removeFromSuperview]; + } + } else { + _yChartLabels = [NSMutableArray new]; + } + + if (yStep == 0.0) { + PNChartLabel *minLabel = [[PNChartLabel alloc] initWithFrame:CGRectMake(0.0, (NSInteger) _chartCavanHeight, (NSInteger) _chartMarginBottom, (NSInteger) _yLabelHeight)]; + minLabel.text = [self formatYLabel:0.0]; + [self setCustomStyleForYLabel:minLabel]; + [self addSubview:minLabel]; + [_yChartLabels addObject:minLabel]; + + PNChartLabel *midLabel = [[PNChartLabel alloc] initWithFrame:CGRectMake(0.0, (NSInteger) (_chartCavanHeight / 2), (NSInteger) _chartMarginBottom, (NSInteger) _yLabelHeight)]; + midLabel.text = [self formatYLabel:_yValueMax]; + [self setCustomStyleForYLabel:midLabel]; + [self addSubview:midLabel]; + [_yChartLabels addObject:midLabel]; + + PNChartLabel *maxLabel = [[PNChartLabel alloc] initWithFrame:CGRectMake(0.0, 0.0, (NSInteger) _chartMarginBottom, (NSInteger) _yLabelHeight)]; + maxLabel.text = [self formatYLabel:_yValueMax * 2]; + [self setCustomStyleForYLabel:maxLabel]; + [self addSubview:maxLabel]; + [_yChartLabels addObject:maxLabel]; + + } else { + NSInteger index = 0; + NSInteger num = _yLabelNum + 1; + + while (num > 0) { + CGRect labelFrame = CGRectMake(0.0, + (NSInteger) (_chartCavanHeight + _chartMarginTop - index * yStepHeight), + (CGFloat) ((NSInteger) _chartMarginLeft * 0.9), + (NSInteger) _yLabelHeight); + PNChartLabel *label = [[PNChartLabel alloc] initWithFrame:labelFrame]; + [label setTextAlignment:NSTextAlignmentRight]; + label.text = [self formatYLabel:_yValueMin + (yStep * index)]; + [self setCustomStyleForYLabel:label]; + [self addSubview:label]; + [_yChartLabels addObject:label]; + index += 1; + num -= 1; + } + } +} + +- (void)setYLabels:(NSArray *)yLabels { + _showGenYLabels = NO; + _yLabelNum = yLabels.count - 1; + + CGFloat yLabelHeight; + if (_showLabel) { + yLabelHeight = _chartCavanHeight / [yLabels count]; + } else { + yLabelHeight = (self.frame.size.height) / [yLabels count]; + } + + return [self setYLabels:yLabels withHeight:yLabelHeight]; +} + +- (void)setYLabels:(NSArray *)yLabels withHeight:(CGFloat)height { + _yLabels = yLabels; + _yLabelHeight = height; + if (_yChartLabels) { + for (PNChartLabel *label in _yChartLabels) { + [label removeFromSuperview]; + } + } else { + _yChartLabels = [NSMutableArray new]; + } + + NSString *labelText; + + if (_showLabel) { + CGFloat yStepHeight = _chartCavanHeight / _yLabelNum; + + for (int index = 0; index < yLabels.count; index++) { + labelText = yLabels[(NSUInteger) index]; + + NSInteger y = (NSInteger) (_chartCavanHeight + _chartMarginTop - index * yStepHeight); + + PNChartLabel *label = [[PNChartLabel alloc] initWithFrame:CGRectMake(0.0, y, (CGFloat) ((NSInteger) _chartMarginLeft * 0.9), (NSInteger) _yLabelHeight)]; + [label setTextAlignment:NSTextAlignmentRight]; + label.text = labelText; + [self setCustomStyleForYLabel:label]; + [self addSubview:label]; + [_yChartLabels addObject:label]; + } + } +} + +- (CGFloat)computeEqualWidthForXLabels:(NSArray *)xLabels { + CGFloat xLabelWidth; + + if (_showLabel) { + xLabelWidth = _chartCavanWidth / [xLabels count]; + } else { + xLabelWidth = (self.frame.size.width) / [xLabels count]; + } + + return xLabelWidth; +} + + +- (void)setXLabels:(NSArray *)xLabels { + CGFloat xLabelWidth; + + if (_showLabel) { + xLabelWidth = _chartCavanWidth / [xLabels count]; + } else { + xLabelWidth = (self.frame.size.width - _chartMarginLeft - _chartMarginRight) / [xLabels count]; + } + + return [self setXLabels:xLabels withWidth:xLabelWidth]; +} + +- (void)setXLabels:(NSArray *)xLabels withWidth:(CGFloat)width { + _xLabels = xLabels; + _xLabelWidth = width; + if (_xChartLabels) { + for (PNChartLabel *label in _xChartLabels) { + [label removeFromSuperview]; + } + } else { + _xChartLabels = [NSMutableArray new]; + } + + NSString *labelText; + + if (_showLabel) { + for (NSUInteger index = 0; index < xLabels.count; index++) { + labelText = xLabels[index]; + + NSInteger x = (NSInteger) (index * _xLabelWidth + _chartMarginLeft); + NSInteger y = (NSInteger) (_chartMarginBottom + _chartCavanHeight); + + PNChartLabel *label = [[PNChartLabel alloc] initWithFrame:CGRectMake(x, y, (NSInteger) _xLabelWidth, (NSInteger) _chartMarginBottom)]; + [label setTextAlignment:NSTextAlignmentCenter]; + label.text = labelText; + [self setCustomStyleForXLabel:label]; + [self addSubview:label]; + [_xChartLabels addObject:label]; + } + } +} + +- (void)setCustomStyleForXLabel:(UILabel *)label { + if (_xLabelFont) { + label.font = _xLabelFont; + } + + if (_xLabelColor) { + label.textColor = _xLabelColor; + } + +} + +- (void)setCustomStyleForYLabel:(UILabel *)label { + if (_yLabelFont) { + label.font = _yLabelFont; + } + + if (_yLabelColor) { + label.textColor = _yLabelColor; + } +} + +#pragma mark - Touch at point + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [self touchPoint:touches withEvent:event]; + [self touchKeyPoint:touches withEvent:event]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + [self touchPoint:touches withEvent:event]; + [self touchKeyPoint:touches withEvent:event]; +} + +- (void)touchPoint:(NSSet *)touches withEvent:(UIEvent *)event { + // Get the point user touched + UITouch *touch = [touches anyObject]; + CGPoint touchPoint = [touch locationInView:self]; + + for (NSUInteger p = 0; p < _pathPoints.count; p++) { + NSArray *linePointsArray = _endPointsOfPath[p]; + + for (NSUInteger i = 0; i < (int) linePointsArray.count - 1; i += 2) { + CGPoint p1 = [linePointsArray[i] CGPointValue]; + CGPoint p2 = [linePointsArray[i + 1] CGPointValue]; + + // Closest distance from point to line + float distance = (float) fabs(((p2.x - p1.x) * (touchPoint.y - p1.y)) - ((p1.x - touchPoint.x) * (p1.y - p2.y))); + distance /= hypot(p2.x - p1.x, p1.y - p2.y); + + if (distance <= 5.0) { + // Conform to delegate parameters, figure out what bezier path this CGPoint belongs to. + NSUInteger lineIndex = 0; + for (NSArray<UIBezierPath *> *paths in _chartPath) { + for (UIBezierPath *path in paths) { + BOOL pointContainsPath = CGPathContainsPoint(path.CGPath, NULL, p1, NO); + if (pointContainsPath) { + [_delegate userClickedOnLinePoint:touchPoint lineIndex:lineIndex]; + return; + } + } + lineIndex++; + } + } + } + } +} + +- (void)touchKeyPoint:(NSSet *)touches withEvent:(UIEvent *)event { + // Get the point user touched + UITouch *touch = [touches anyObject]; + CGPoint touchPoint = [touch locationInView:self]; + + for (NSUInteger p = 0; p < _pathPoints.count; p++) { + NSArray *linePointsArray = _pathPoints[p]; + + for (NSUInteger i = 0; i < (int) linePointsArray.count - 1; i += 1) { + CGPoint p1 = [linePointsArray[i] CGPointValue]; + CGPoint p2 = [linePointsArray[i + 1] CGPointValue]; + + float distanceToP1 = (float) fabs(hypot(touchPoint.x - p1.x, touchPoint.y - p1.y)); + float distanceToP2 = (float) hypot(touchPoint.x - p2.x, touchPoint.y - p2.y); + + float distance = MIN(distanceToP1, distanceToP2); + + if (distance <= 10.0) { + [_delegate userClickedOnLineKeyPoint:touchPoint + lineIndex:p + pointIndex:(distance == distanceToP2 ? i + 1 : i)]; + + return; + } + } + } +} + +#pragma mark - Draw Chart + +- (void)populateChartLines { + for (NSUInteger lineIndex = 0; lineIndex < self.chartData.count; lineIndex++) { + NSArray<UIBezierPath *> *progressLines = self.chartPath[lineIndex]; + // each chart line can be divided into multiple paths because + // we need ot draw each path with different color + // if there is not rangeColors then there is only one progressLinePath per chart + NSArray<UIColor *> *progressLineColors = self.progressLinePathsColors[lineIndex]; + [self.chartLineArray[lineIndex] removeAllObjects]; + NSUInteger progressLineIndex = 0;; + for (UIBezierPath *progressLinePath in progressLines) { + PNLineChartData *chartData = self.chartData[lineIndex]; + CAShapeLayer *chartLine = [CAShapeLayer layer]; + chartLine.lineCap = kCALineCapButt; + chartLine.lineJoin = kCALineJoinMiter; + chartLine.fillColor = self.backgroundColor.CGColor; + chartLine.lineWidth = chartData.lineWidth; + chartLine.path = progressLinePath.CGPath; + chartLine.strokeEnd = 0.0; + chartLine.strokeColor = progressLineColors[progressLineIndex].CGColor; + [self.layer addSublayer:chartLine]; + [self.chartLineArray[lineIndex] addObject:chartLine]; + progressLineIndex++; + } + } +} + +/* + * strokeChart should remove the previously drawn chart lines and points + * and then proceed to draw the new lines + */ +- (void)strokeChart { + [self removeLayers]; + // remove all shape layers before adding new ones + [self recreatePointLayers]; + // Cavan height and width needs to be set before + // setNeedsDisplay is invoked because setNeedsDisplay + // will invoke drawRect and if Cavan dimensions is not + // set the chart will be misplaced + [self resetCavanHeight]; + [self prepareYLabelsWithData:_chartData]; + + _chartPath = [[NSMutableArray alloc] init]; + _pointPath = [[NSMutableArray alloc] init]; + _gradeStringPaths = [NSMutableArray array]; + _progressLinePathsColors = [[NSMutableArray alloc] init]; + + [self calculateChartPath:_chartPath + andPointsPath:_pointPath + andPathKeyPoints:_pathPoints + andPathStartEndPoints:_endPointsOfPath + andProgressLinePathsColors:_progressLinePathsColors]; + [self populateChartLines]; + // Draw each line + for (NSUInteger lineIndex = 0; lineIndex < self.chartData.count; lineIndex++) { + PNLineChartData *chartData = self.chartData[lineIndex]; + NSArray<CAShapeLayer *> *chartLines =self.chartLineArray[lineIndex]; + CAShapeLayer *pointLayer = (CAShapeLayer *) self.chartPointArray[lineIndex]; + UIGraphicsBeginImageContext(self.frame.size); + if (chartData.inflexionPointColor) { + pointLayer.strokeColor = [[chartData.inflexionPointColor + colorWithAlphaComponent:chartData.alpha] CGColor]; + } else { + pointLayer.strokeColor = [PNGreen CGColor]; + } + // setup the color of the chart line + NSArray<UIBezierPath *> *progressLines = _chartPath[lineIndex]; + UIBezierPath *pointPath = _pointPath[lineIndex]; + + pointLayer.path = pointPath.CGPath; + + [CATransaction begin]; + for (NSUInteger index = 0; index < progressLines.count; index++) { + CAShapeLayer *chartLine = chartLines[index]; + //chartLine strokeColor is already set. no need to override here + [chartLine addAnimation:self.pathAnimation forKey:@"strokeEndAnimation"]; + chartLine.strokeEnd = 1.0; + } + + // if you want cancel the point animation, comment this code, the point will show immediately + if (chartData.inflexionPointStyle != PNLineChartPointStyleNone) { + [pointLayer addAnimation:self.pathAnimation forKey:@"strokeEndAnimation"]; + } + + [CATransaction commit]; + + NSMutableArray *textLayerArray = self.gradeStringPaths[lineIndex]; + for (CATextLayer *textLayer in textLayerArray) { + CABasicAnimation *fadeAnimation = [self fadeAnimation]; + [textLayer addAnimation:fadeAnimation forKey:nil]; + } + + UIGraphicsEndImageContext(); + } + [self setNeedsDisplay]; +} + + +- (void)calculateChartPath:(NSMutableArray *)chartPath + andPointsPath:(NSMutableArray *)pointsPath + andPathKeyPoints:(NSMutableArray *)pathPoints + andPathStartEndPoints:(NSMutableArray *)pointsOfPath +andProgressLinePathsColors:(NSMutableArray *)progressLinePathsColors { + + // Draw each line + + for (NSUInteger lineIndex = 0; lineIndex < self.chartData.count; lineIndex++) { + PNLineChartData *chartData = self.chartData[lineIndex]; + + CGFloat yValue; + NSMutableArray<UIBezierPath *> *progressLines = [NSMutableArray new]; + NSMutableArray<UIColor *> *progressLineColors = [NSMutableArray new]; + + UIBezierPath *pointPath = [UIBezierPath bezierPath]; + + + [chartPath insertObject:progressLines atIndex:lineIndex]; + [pointsPath insertObject:pointPath atIndex:lineIndex]; + [progressLinePathsColors insertObject:progressLineColors atIndex:lineIndex]; + + + NSMutableArray *gradePathArray = [NSMutableArray array]; + [self.gradeStringPaths addObject:gradePathArray]; + + NSMutableArray *linePointsArray = [[NSMutableArray alloc] init]; + NSMutableArray *lineStartEndPointsArray = [[NSMutableArray alloc] init]; + int last_x = 0; + int last_y = 0; + NSMutableArray<NSDictionary<NSString *, NSValue *> *> *progressLinePaths = [NSMutableArray new]; + UIColor *defaultColor = chartData.color != nil ? chartData.color : [UIColor greenColor]; + CGFloat inflexionWidth = chartData.inflexionPointWidth; + + for (NSUInteger i = 0; i < chartData.itemCount; i++) { + + NSValue *from = nil; + NSValue *to = nil; + + yValue = chartData.getData(i).y; + + int x = (int) (i * _xLabelWidth + _chartMarginLeft + _xLabelWidth / 2.0); + int y = (int)[self yValuePositionInLineChart:yValue]; + + // Circular point + if (chartData.inflexionPointStyle == PNLineChartPointStyleCircle) + { + + CGRect circleRect = CGRectMake(x - inflexionWidth / 2, y - inflexionWidth / 2, inflexionWidth, inflexionWidth); + CGPoint circleCenter = CGPointMake(circleRect.origin.x + (circleRect.size.width / 2), circleRect.origin.y + (circleRect.size.height / 2)); + + [pointPath moveToPoint:CGPointMake(circleCenter.x + (inflexionWidth / 2), circleCenter.y)]; + [pointPath addArcWithCenter:circleCenter radius:inflexionWidth / 2 startAngle:0 endAngle:(CGFloat) (2 * M_PI) clockwise:YES]; + + //jet text display text + if (chartData.showPointLabel) { + [gradePathArray addObject:[self createPointLabelFor:chartData.getData(i).rawY pointCenter:circleCenter width:inflexionWidth withChartData:chartData]]; + } + + if (i > 0) { + + // calculate the point for line + float distance = (float) sqrt(pow(x - last_x, 2) + pow(y - last_y, 2)); + float last_x1 = last_x + (inflexionWidth / 2) / distance * (x - last_x); + float last_y1 = last_y + (inflexionWidth / 2) / distance * (y - last_y); + float x1 = x - (inflexionWidth / 2) / distance * (x - last_x); + float y1 = y - (inflexionWidth / 2) / distance * (y - last_y); + from = [NSValue valueWithCGPoint:CGPointMake(last_x1, last_y1)]; + to = [NSValue valueWithCGPoint:CGPointMake(x1, y1)]; + } + } + // Square point + else if (chartData.inflexionPointStyle == PNLineChartPointStyleSquare) { + + CGRect squareRect = CGRectMake(x - inflexionWidth / 2, y - inflexionWidth / 2, inflexionWidth, inflexionWidth); + CGPoint squareCenter = CGPointMake(squareRect.origin.x + (squareRect.size.width / 2), squareRect.origin.y + (squareRect.size.height / 2)); + + [pointPath moveToPoint:CGPointMake(squareCenter.x - (inflexionWidth / 2), squareCenter.y - (inflexionWidth / 2))]; + [pointPath addLineToPoint:CGPointMake(squareCenter.x + (inflexionWidth / 2), squareCenter.y - (inflexionWidth / 2))]; + [pointPath addLineToPoint:CGPointMake(squareCenter.x + (inflexionWidth / 2), squareCenter.y + (inflexionWidth / 2))]; + [pointPath addLineToPoint:CGPointMake(squareCenter.x - (inflexionWidth / 2), squareCenter.y + (inflexionWidth / 2))]; + [pointPath closePath]; + + // text display text + if (chartData.showPointLabel) { + [gradePathArray addObject:[self createPointLabelFor:chartData.getData(i).rawY pointCenter:squareCenter width:inflexionWidth withChartData:chartData]]; + } + + if (i > 0) { + + // calculate the point for line + float distance = (float) sqrt(pow(x - last_x, 2) + pow(y - last_y, 2)); + float last_x1 = last_x + (inflexionWidth / 2); + float last_y1 = last_y + (inflexionWidth / 2) / distance * (y - last_y); + float x1 = x - (inflexionWidth / 2); + float y1 = y - (inflexionWidth / 2) / distance * (y - last_y); + from = [NSValue valueWithCGPoint:CGPointMake(last_x1, last_y1)]; + to = [NSValue valueWithCGPoint:CGPointMake(x1, y1)]; + } + } + // Triangle point + else if (chartData.inflexionPointStyle == PNLineChartPointStyleTriangle) { + + CGRect squareRect = CGRectMake(x - inflexionWidth / 2, y - inflexionWidth / 2, inflexionWidth, inflexionWidth); + + CGPoint startPoint = CGPointMake(squareRect.origin.x, squareRect.origin.y + squareRect.size.height); + CGPoint endPoint = CGPointMake(squareRect.origin.x + (squareRect.size.width / 2), squareRect.origin.y); + CGPoint middlePoint = CGPointMake(squareRect.origin.x + (squareRect.size.width), squareRect.origin.y + squareRect.size.height); + + [pointPath moveToPoint:startPoint]; + [pointPath addLineToPoint:middlePoint]; + [pointPath addLineToPoint:endPoint]; + [pointPath closePath]; + + // text display text + if (chartData.showPointLabel) { + [gradePathArray addObject:[self createPointLabelFor:chartData.getData(i).rawY pointCenter:middlePoint width:inflexionWidth withChartData:chartData]]; + } + + if (i > 0) { + // calculate the point for triangle + float distance = (float) (sqrt(pow(x - last_x, 2) + pow(y - last_y, 2)) * 1.4); + float last_x1 = last_x + (inflexionWidth / 2) / distance * (x - last_x); + float last_y1 = last_y + (inflexionWidth / 2) / distance * (y - last_y); + float x1 = x - (inflexionWidth / 2) / distance * (x - last_x); + float y1 = y - (inflexionWidth / 2) / distance * (y - last_y); + from = [NSValue valueWithCGPoint:CGPointMake(last_x1, last_y1)]; + to = [NSValue valueWithCGPoint:CGPointMake(x1, y1)]; + } + } else { + + if (i > 0) { + from = [NSValue valueWithCGPoint:CGPointMake(last_x, last_y)]; + to = [NSValue valueWithCGPoint:CGPointMake(x, y)]; + } + } + if(from != nil && to != nil) { + [progressLinePaths addObject:@{@"from": from, @"to":to}]; + [lineStartEndPointsArray addObject:from]; + [lineStartEndPointsArray addObject:to]; + } + [linePointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(x, y)]]; + last_x = x; + last_y = y; + } + + [pointsOfPath addObject:[lineStartEndPointsArray copy]]; + [pathPoints addObject:[linePointsArray copy]]; + // if rangeColors is not nil then it means we need to draw the chart + // with different colors. colorRangesBetweenP1.. function takes care of + // partitioning the p1->p2 into segments from which we can create UIBezierPath + if (self.showSmoothLines && chartData.itemCount >= 4) { + for (NSDictionary<NSString *, NSValue *> *item in progressLinePaths) { + NSArray<NSDictionary *> *calculatedRanges = + [self colorRangesBetweenP1:[item[@"from"] CGPointValue] + P2:[item[@"to"] CGPointValue] + rangeColors:chartData.rangeColors + defaultColor:defaultColor]; + for (NSDictionary *range in calculatedRanges) { +// NSLog(@"range : %@ range: %@ color %@", range[@"from"], range[@"to"], range[@"color"]); + UIBezierPath *currentProgressLine = [UIBezierPath bezierPath]; + CGPoint segmentP1 = [range[@"from"] CGPointValue]; + CGPoint segmentP2 = [range[@"to"] CGPointValue]; + [currentProgressLine moveToPoint:segmentP1]; + CGPoint midPoint = [PNLineChart midPointBetweenPoint1:segmentP1 andPoint2:segmentP2]; + [currentProgressLine addQuadCurveToPoint:midPoint + controlPoint:[PNLineChart controlPointBetweenPoint1:midPoint andPoint2:segmentP1]]; + [currentProgressLine addQuadCurveToPoint:segmentP2 + controlPoint:[PNLineChart controlPointBetweenPoint1:midPoint andPoint2:segmentP2]]; + [progressLines addObject:currentProgressLine]; + [progressLineColors addObject:range[@"color"]]; + } + } + } else { + for (NSDictionary<NSString *, NSValue *> *item in progressLinePaths) { + NSArray<NSDictionary *> *calculatedRanges = + [self colorRangesBetweenP1:[item[@"from"] CGPointValue] + P2:[item[@"to"] CGPointValue] + rangeColors:chartData.rangeColors + defaultColor:defaultColor]; + for (NSDictionary *range in calculatedRanges) { +// NSLog(@"range : %@ range: %@ color %@", range[@"from"], range[@"to"], range[@"color"]); + UIBezierPath *currentProgressLine = [UIBezierPath bezierPath]; + [currentProgressLine moveToPoint:[range[@"from"] CGPointValue]]; + [currentProgressLine addLineToPoint:[range[@"to"] CGPointValue]]; + [progressLines addObject:currentProgressLine]; + [progressLineColors addObject:range[@"color"]]; + } + } + } + } +} + +#pragma mark - Set Chart Data + +- (void)setChartData:(NSArray *)data { + if (data != _chartData) { + _chartData = data; + } +} + + +- (void)removeLayers { + for (NSArray<CALayer *> *layers in self.chartLineArray) { + for (CALayer *layer in layers) { + [layer removeFromSuperlayer]; + } + } + for (CALayer *layer in self.chartPointArray) { + [layer removeFromSuperlayer]; + } + self.chartLineArray = [NSMutableArray arrayWithCapacity:_chartData.count]; + self.chartPointArray = [NSMutableArray arrayWithCapacity:_chartData.count]; +} + +-(void) resetCavanHeight { + _chartCavanHeight = self.frame.size.height - _chartMarginBottom - _chartMarginTop; + if (!_showLabel) { + _chartCavanHeight = self.frame.size.height - 2 * _yLabelHeight; + _chartCavanWidth = self.frame.size.width; + //_chartMargin = chartData.inflexionPointWidth; + _xLabelWidth = (_chartCavanWidth / ([_xLabels count])); + } +} + +- (void)recreatePointLayers { + for (PNLineChartData *chartData in _chartData) { + // create as many chart line layers as there are data-lines + [self.chartLineArray addObject:[NSMutableArray new]]; + + // create point + CAShapeLayer *pointLayer = [CAShapeLayer layer]; + pointLayer.strokeColor = [[chartData.color colorWithAlphaComponent:chartData.alpha] CGColor]; + pointLayer.lineCap = kCALineCapRound; + pointLayer.lineJoin = kCALineJoinBevel; + pointLayer.fillColor = nil; + pointLayer.lineWidth = chartData.lineWidth; + [self.layer addSublayer:pointLayer]; + [self.chartPointArray addObject:pointLayer]; + } +} + +- (void)prepareYLabelsWithData:(NSArray *)data { + CGFloat yMax = 0.0f; + CGFloat yMin = MAXFLOAT; + NSMutableArray *yLabelsArray = [NSMutableArray new]; + + for (PNLineChartData *chartData in data) { + // create as many chart line layers as there are data-lines + + for (NSUInteger i = 0; i < chartData.itemCount; i++) { + CGFloat yValue = chartData.getData(i).y; + [yLabelsArray addObject:[NSString stringWithFormat:@"%2f", yValue]]; + yMax = fmaxf(yMax, yValue); + yMin = fminf(yMin, yValue); + } + } + + + if (_yValueMin == -FLT_MAX) { + _yValueMin = (_yFixedValueMin > -FLT_MAX) ? _yFixedValueMin : yMin; + } + if (_yValueMax == -FLT_MAX) { + _yValueMax = (CGFloat) ((_yFixedValueMax > -FLT_MAX) ? _yFixedValueMax : yMax + yMax / 10.0); + } + + if (_showGenYLabels) { + [self setYLabels]; + } + +} + +#pragma mark - Update Chart Data + +- (void)updateChartData:(NSArray *)data { + _chartData = data; + + [self prepareYLabelsWithData:data]; + + [self calculateChartPath:_chartPath + andPointsPath:_pointPath + andPathKeyPoints:_pathPoints + andPathStartEndPoints:_endPointsOfPath + andProgressLinePathsColors:_progressLinePathsColors]; + + for (NSUInteger lineIndex = 0; lineIndex < self.chartData.count; lineIndex++) { + + CAShapeLayer *chartLine = (CAShapeLayer *) self.chartLineArray[lineIndex]; + CAShapeLayer *pointLayer = (CAShapeLayer *) self.chartPointArray[lineIndex]; + + + NSArray<UIBezierPath *> *progressLines = _chartPath[lineIndex]; + UIBezierPath *pointPath = _pointPath[lineIndex]; + + for(UIBezierPath *progressLine in progressLines) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; + pathAnimation.fromValue = (id) chartLine.path; + pathAnimation.toValue = (__bridge id) [progressLine CGPath]; + pathAnimation.duration = 0.5f; + pathAnimation.autoreverses = NO; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + [chartLine addAnimation:pathAnimation forKey:@"animationKey"]; + chartLine.path = progressLine.CGPath; + } + + CABasicAnimation *pointPathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; + pointPathAnimation.fromValue = (id) pointLayer.path; + pointPathAnimation.toValue = (__bridge id) [pointPath CGPath]; + pointPathAnimation.duration = 0.5f; + pointPathAnimation.autoreverses = NO; + pointPathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + [pointLayer addAnimation:pointPathAnimation forKey:@"animationKey"]; + + pointLayer.path = pointPath.CGPath; + + + } + +} + +#define IOS7_OR_LATER [[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 + +- (void)drawRect:(CGRect)rect { + if (self.isShowCoordinateAxis) { + CGFloat yAxisOffset = 10.f; + + CGContextRef ctx = UIGraphicsGetCurrentContext(); + UIGraphicsPopContext(); + UIGraphicsPushContext(ctx); + CGContextSetLineWidth(ctx, self.axisWidth); + CGContextSetStrokeColorWithColor(ctx, [self.axisColor CGColor]); + + CGFloat xAxisWidth = CGRectGetWidth(rect) - (_chartMarginLeft + _chartMarginRight) / 2; + CGFloat yAxisHeight = _chartMarginBottom + _chartCavanHeight; + + // draw coordinate axis + CGContextMoveToPoint(ctx, _chartMarginBottom + yAxisOffset, 0); + CGContextAddLineToPoint(ctx, _chartMarginBottom + yAxisOffset, yAxisHeight); + CGContextAddLineToPoint(ctx, xAxisWidth, yAxisHeight); + CGContextStrokePath(ctx); + + // draw y axis arrow + CGContextMoveToPoint(ctx, _chartMarginBottom + yAxisOffset - 3, 6); + CGContextAddLineToPoint(ctx, _chartMarginBottom + yAxisOffset, 0); + CGContextAddLineToPoint(ctx, _chartMarginBottom + yAxisOffset + 3, 6); + CGContextStrokePath(ctx); + + // draw x axis arrow + CGContextMoveToPoint(ctx, xAxisWidth - 6, yAxisHeight - 3); + CGContextAddLineToPoint(ctx, xAxisWidth, yAxisHeight); + CGContextAddLineToPoint(ctx, xAxisWidth - 6, yAxisHeight + 3); + CGContextStrokePath(ctx); + + if (self.showLabel) { + + // draw x axis separator + CGPoint point; + for (NSUInteger i = 0; i < [self.xLabels count]; i++) { + point = CGPointMake(2 * _chartMarginLeft + (i * _xLabelWidth), _chartMarginBottom + _chartCavanHeight); + CGContextMoveToPoint(ctx, point.x, point.y - 2); + CGContextAddLineToPoint(ctx, point.x, point.y); + CGContextStrokePath(ctx); + } + + // draw y axis separator + CGFloat yStepHeight = _chartCavanHeight / _yLabelNum; + for (NSUInteger i = 0; i < [self.xLabels count]; i++) { + point = CGPointMake(_chartMarginBottom + yAxisOffset, (_chartCavanHeight - i * yStepHeight + _yLabelHeight / 2)); + CGContextMoveToPoint(ctx, point.x, point.y); + CGContextAddLineToPoint(ctx, point.x + 2, point.y); + CGContextStrokePath(ctx); + } + } + + UIFont *font = [UIFont systemFontOfSize:11]; + + // draw y unit + if ([self.yUnit length]) { + CGFloat height = [PNLineChart sizeOfString:self.yUnit withWidth:30.f font:font].height; + CGRect drawRect = CGRectMake(_chartMarginLeft + 10 + 5, 0, 30.f, height); + [self drawTextInContext:ctx text:self.yUnit inRect:drawRect font:font color:self.yLabelColor]; + } + + // draw x unit + if ([self.xUnit length]) { + CGFloat height = [PNLineChart sizeOfString:self.xUnit withWidth:30.f font:font].height; + CGRect drawRect = CGRectMake(CGRectGetWidth(rect) - _chartMarginLeft + 5, _chartMarginBottom + _chartCavanHeight - height / 2, 25.f, height); + [self drawTextInContext:ctx text:self.xUnit inRect:drawRect font:font color:self.xLabelColor]; + } + } + if (self.showYGridLines) { + CGContextRef ctx = UIGraphicsGetCurrentContext(); + CGFloat yAxisOffset = _showLabel ? 10.f : 0.0f; + CGPoint point; + CGFloat yStepHeight = _chartCavanHeight / _yLabelNum; + if (self.yGridLinesColor) { + CGContextSetStrokeColorWithColor(ctx, self.yGridLinesColor.CGColor); + } else { + CGContextSetStrokeColorWithColor(ctx, [UIColor lightGrayColor].CGColor); + } + for (NSUInteger i = 0; i < _yLabelNum; i++) { + point = CGPointMake(_chartMarginLeft + yAxisOffset, (_chartCavanHeight - i * yStepHeight + _yLabelHeight / 2)); + CGContextMoveToPoint(ctx, point.x, point.y); + // add dotted style grid + CGFloat dash[] = {6, 5}; + // dot diameter is 20 points + CGContextSetLineWidth(ctx, 0.5); + CGContextSetLineCap(ctx, kCGLineCapRound); + CGContextSetLineDash(ctx, 0.0, dash, 2); + CGContextAddLineToPoint(ctx, CGRectGetWidth(rect) - _chartMarginLeft + 5, point.y); + CGContextStrokePath(ctx); + } + } + + [super drawRect:rect]; +} + +#pragma mark private methods + +/* + * helper function that maps a y value ( from chartData) to + * a position in the chart ( between _yValueMin and _yValueMax) + */ +- (CGFloat)yValuePositionInLineChart:(CGFloat)y { + CGFloat innerGrade; + if (!(_yValueMax - _yValueMin)) { + innerGrade = 0.5; + } else { + innerGrade = ((CGFloat) y - _yValueMin) / (_yValueMax - _yValueMin); + } + return _chartCavanHeight - (innerGrade * _chartCavanHeight) - (_yLabelHeight / 2) + _chartMarginTop; +} + +/** + * return array of segments which represents the color and path + * for each segments. + * for instance if p1.y=1 and p2.y=10 + * and rangeColor = use blue for 2<y<3 and red for 4<y<6 + * then this function divides the space between p1 and p2 into three segments + * segment #1 : 1-2 : default color + * segment #2 : 2-3 : blue + * segment #3 : 3-4 : default color + * segment #4 : 4-6 : red + * segment #5: 6-10 : default color + * + * keep in mind that the rangeColors values are based on the chartData so it needs to + * convert those values to coordinates which are valid between yValueMin and yValueMax + * + * in order to find whether there is an overlap between any of the rangeColors and the + * p1-p2 it uses NSIntersectionRange to intersect their yValues. + * + * @param p1 + * @param p2 + * @param rangeColors + * @param defaultColor + * @return + */ +- (NSArray *)colorRangesBetweenP1:(CGPoint)p1 P2:(CGPoint)p2 + rangeColors:(NSArray<PNLineChartColorRange *> *)rangeColors + defaultColor:(UIColor *)defaultColor { + if (rangeColors && rangeColors.count > 0 && p2.x > p1.x) { + PNLineChartColorRange *colorForRangeInfo = [[rangeColors firstObject] copy]; + NSArray *remainingRanges = nil; + if (rangeColors.count > 1) { + remainingRanges = [rangeColors subarrayWithRange:NSMakeRange(1, rangeColors.count - 1)]; + } + // tRange : convert the rangeColors.range values to value between yValueMin and yValueMax + CGFloat transformedStart = [self yValuePositionInLineChart:(CGFloat) + colorForRangeInfo.range.location]; + CGFloat transformedEnd = [self yValuePositionInLineChart:(CGFloat) + (colorForRangeInfo.range.location + colorForRangeInfo.range.length)]; + + NSRange pathRange = NSMakeRange((NSUInteger) fmin(p1.y, p2.y), (NSUInteger) fabs(p2.y - p1.y)); + NSRange tRange = NSMakeRange((NSUInteger) fmin(transformedStart, transformedEnd), + (NSUInteger) fabs(transformedEnd - transformedStart)); + if (NSIntersectionRange(tRange, pathRange).length > 0) { + CGPoint partition1EndPoint; + CGPoint partition2EndPoint; + NSArray *partition1 = @[]; + NSDictionary *partition2 = nil; + NSArray *partition3 = @[]; + if (p2.y >= p1.y) { + partition1EndPoint = CGPointMake([PNLineChart xOfY:(CGFloat) fmax(p1.y, tRange.location) + betweenPoint1:p1 + andPoint2:p2], (CGFloat) fmax(p1.y, tRange.location)); + partition2EndPoint = CGPointMake([PNLineChart xOfY:(CGFloat) fmin(p2.y, tRange.location + tRange.length) + betweenPoint1:p1 + andPoint2:p2], (CGFloat) fmin(p2.y, tRange.location + tRange.length)); + } else { + partition1EndPoint = CGPointMake([PNLineChart xOfY:(CGFloat) fmin(p1.y, tRange.location + tRange.length) + betweenPoint1:p1 + andPoint2:p2], (CGFloat) fmin(p1.y, tRange.location + tRange.length)); + partition2EndPoint = CGPointMake([PNLineChart xOfY:(CGFloat) fmax(p2.y, tRange.location) + betweenPoint1:p1 + andPoint2:p2], (CGFloat) fmax(p2.y, tRange.location)); + } + if (p1.y != partition1EndPoint.y) { + partition1 = [self colorRangesBetweenP1:p1 + P2:partition1EndPoint + rangeColors:remainingRanges + defaultColor:defaultColor]; + } + partition2 = @{ + @"color": colorForRangeInfo.color, + @"from": [NSValue valueWithCGPoint:partition1EndPoint], + @"to": [NSValue valueWithCGPoint:partition2EndPoint]}; + if (p2.y != partition2EndPoint.y) { + partition3 = [self colorRangesBetweenP1:partition2EndPoint + P2:p2 + rangeColors:remainingRanges + defaultColor:defaultColor]; + } + return [[partition1 arrayByAddingObject:partition2] arrayByAddingObjectsFromArray:partition3]; + } else { + + return [self colorRangesBetweenP1:p1 + P2:p2 + rangeColors:remainingRanges + defaultColor:defaultColor]; + } + } else { + return @[@{ + @"color": defaultColor, + @"from": [NSValue valueWithCGPoint:p1], + @"to": [NSValue valueWithCGPoint:p2]}]; + } +} + + +- (void)setupDefaultValues { + [super setupDefaultValues]; + // Initialization code + self.backgroundColor = [UIColor whiteColor]; + self.clipsToBounds = YES; + self.chartLineArray = [NSMutableArray new]; + _showLabel = YES; + _showGenYLabels = YES; + _pathPoints = [[NSMutableArray alloc] init]; + _endPointsOfPath = [[NSMutableArray alloc] init]; + self.userInteractionEnabled = YES; + + _yFixedValueMin = -FLT_MAX; + _yFixedValueMax = -FLT_MAX; + _yValueMax = -FLT_MAX; + _yValueMin = -FLT_MAX; + _yLabelNum = 5; + _yLabelHeight = [[[[PNChartLabel alloc] init] font] pointSize]; + +// _chartMargin = 40; + + _chartMarginLeft = 25.0; + _chartMarginRight = 25.0; + _chartMarginTop = 25.0; + _chartMarginBottom = 25.0; + + _yLabelFormat = @"%1.f"; + + _chartCavanWidth = self.frame.size.width - _chartMarginLeft - _chartMarginRight; + _chartCavanHeight = self.frame.size.height - _chartMarginBottom - _chartMarginTop; + + // Coordinate Axis Default Values + _showCoordinateAxis = NO; + _axisColor = [UIColor colorWithRed:0.4f green:0.4f blue:0.4f alpha:1.f]; + _axisWidth = 1.f; + + // do not create curved line chart by default + _showSmoothLines = NO; + +} + +#pragma mark - tools + ++ (CGSize)sizeOfString:(NSString *)text withWidth:(float)width font:(UIFont *)font { + CGSize size = CGSizeMake(width, MAXFLOAT); + + if ([text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) { + NSDictionary *tdic = @{NSFontAttributeName: font}; + size = [text boundingRectWithSize:size + options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading + attributes:tdic + context:nil].size; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + size = [text sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByCharWrapping]; +#pragma clang diagnostic pop + } + + return size; +} + ++ (CGPoint)midPointBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2 { + return CGPointMake((point1.x + point2.x) / 2, (point1.y + point2.y) / 2); +} + ++ (CGFloat)xOfY:(CGFloat)y betweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2 { + CGFloat m = (point2.y - point1.y) / (point2.x - point1.x); + // formulate = y - y1 = m (x - x1) = mx - mx1 -> mx = y - y1 + mx1 -> + // x = (y - y1 + mx1) / m + return (y - point1.y + m * point1.x) / m; +} + + ++ (CGPoint)controlPointBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2 { + CGPoint controlPoint = [self midPointBetweenPoint1:point1 andPoint2:point2]; + CGFloat diffY = abs((int) (point2.y - controlPoint.y)); + if (point1.y < point2.y) + controlPoint.y += diffY; + else if (point1.y > point2.y) + controlPoint.y -= diffY; + return controlPoint; +} + +- (void)drawTextInContext:(CGContextRef)ctx text:(NSString *)text inRect:(CGRect)rect font:(UIFont *)font color:(UIColor *)color { + if (IOS7_OR_LATER) { + NSMutableParagraphStyle *priceParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + priceParagraphStyle.lineBreakMode = NSLineBreakByTruncatingTail; + priceParagraphStyle.alignment = NSTextAlignmentLeft; + + if (color != nil) { + [text drawInRect:rect + withAttributes:@{NSParagraphStyleAttributeName: priceParagraphStyle, NSFontAttributeName: font, + NSForegroundColorAttributeName: color}]; + } else { + [text drawInRect:rect + withAttributes:@{NSParagraphStyleAttributeName: priceParagraphStyle, NSFontAttributeName: font}]; + } + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [text drawInRect:rect + withFont:font + lineBreakMode:NSLineBreakByTruncatingTail + alignment:NSTextAlignmentLeft]; +#pragma clang diagnostic pop + } +} + +- (NSString *)formatYLabel:(double)value { + + if (self.yLabelBlockFormatter) { + return self.yLabelBlockFormatter((CGFloat) value); + } else { + if (!self.thousandsSeparator) { + NSString *format = self.yLabelFormat ?: @"%1.f"; + return [NSString stringWithFormat:format, value]; + } + + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; + [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; + return [numberFormatter stringFromNumber:@(value)]; + } +} + +- (UIView *)getLegendWithMaxWidth:(CGFloat)mWidth { + if ([self.chartData count] < 1) { + return nil; + } + + /* This is a short line that refers to the chart data */ + CGFloat legendLineWidth = 40; + + /* x and y are the coordinates of the starting point of each legend item */ + CGFloat x = 0; + CGFloat y = 0; + + /* accumulated height */ + CGFloat totalHeight = 0; + CGFloat totalWidth = 0; + + NSMutableArray *legendViews = [[NSMutableArray alloc] init]; + + /* Determine the max width of each legend item */ + CGFloat maxLabelWidth; + if (self.legendStyle == PNLegendItemStyleStacked) { + maxLabelWidth = mWidth - legendLineWidth; + } else { + maxLabelWidth = MAXFLOAT; + } + + /* this is used when labels wrap text and the line + * should be in the middle of the first row */ + CGFloat singleRowHeight = [PNLineChart sizeOfString:@"Test" + withWidth:MAXFLOAT + font:self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]].height; + + NSUInteger counter = 0; + NSUInteger rowWidth = 0; + NSUInteger rowMaxHeight = 0; + + for (PNLineChartData *pdata in self.chartData) { + /* Expected label size*/ + CGSize labelsize = [PNLineChart sizeOfString:pdata.dataTitle + withWidth:maxLabelWidth + font:self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]]; + + /* draw lines */ + if ((rowWidth + labelsize.width + legendLineWidth > mWidth) && (self.legendStyle == PNLegendItemStyleSerial)) { + rowWidth = 0; + x = 0; + y += rowMaxHeight; + rowMaxHeight = 0; + } + rowWidth += labelsize.width + legendLineWidth; + totalWidth = self.legendStyle == PNLegendItemStyleSerial ? fmaxf(rowWidth, totalWidth) : fmaxf(totalWidth, labelsize.width + legendLineWidth); + + /* If there is inflection decorator, the line is composed of two lines + * and this is the space that separates two lines in order to put inflection + * decorator */ + + CGFloat inflexionWidthSpacer = pdata.inflexionPointStyle == PNLineChartPointStyleTriangle ? pdata.inflexionPointWidth / 2 : pdata.inflexionPointWidth; + + CGFloat halfLineLength; + + if (pdata.inflexionPointStyle != PNLineChartPointStyleNone) { + halfLineLength = (CGFloat) ((legendLineWidth * 0.8 - inflexionWidthSpacer) / 2); + } else { + halfLineLength = (CGFloat) (legendLineWidth * 0.8); + } + + UIView *line = [[UIView alloc] initWithFrame:CGRectMake((CGFloat) (x + legendLineWidth * 0.1), y + (singleRowHeight - pdata.lineWidth) / 2, halfLineLength, pdata.lineWidth)]; + + line.backgroundColor = pdata.color; + line.alpha = pdata.alpha; + [legendViews addObject:line]; + + if (pdata.inflexionPointStyle != PNLineChartPointStyleNone) { + line = [[UIView alloc] initWithFrame:CGRectMake((CGFloat) (x + legendLineWidth * 0.1 + halfLineLength + inflexionWidthSpacer), y + (singleRowHeight - pdata.lineWidth) / 2, halfLineLength, pdata.lineWidth)]; + line.backgroundColor = pdata.color; + line.alpha = pdata.alpha; + [legendViews addObject:line]; + } + + // Add inflexion type + UIColor *inflexionPointColor = pdata.inflexionPointColor; + if (!inflexionPointColor) { + inflexionPointColor = pdata.color; + } + [legendViews addObject:[self drawInflexion:pdata.inflexionPointWidth + center:CGPointMake(x + legendLineWidth / 2, y + singleRowHeight / 2) + strokeWidth:pdata.lineWidth + inflexionStyle:pdata.inflexionPointStyle + andColor:inflexionPointColor + andAlpha:pdata.alpha]]; + + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x + legendLineWidth, y, labelsize.width, labelsize.height)]; + label.text = pdata.dataTitle; + label.textColor = self.legendFontColor ? self.legendFontColor : [UIColor blackColor]; + label.font = self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]; + label.lineBreakMode = NSLineBreakByWordWrapping; + label.numberOfLines = 0; + + rowMaxHeight = (NSUInteger) fmaxf(rowMaxHeight, labelsize.height); + x += self.legendStyle == PNLegendItemStyleStacked ? 0 : labelsize.width + legendLineWidth; + y += self.legendStyle == PNLegendItemStyleStacked ? labelsize.height : 0; + + + totalHeight = self.legendStyle == PNLegendItemStyleSerial ? fmaxf(totalHeight, rowMaxHeight + y) : totalHeight + labelsize.height; + + [legendViews addObject:label]; + counter++; + } + + UIView *legend = [[UIView alloc] initWithFrame:CGRectMake(0, 0, mWidth, totalHeight)]; + + for (UIView *v in legendViews) { + [legend addSubview:v]; + } + return legend; +} + + +- (UIImageView *)drawInflexion:(CGFloat)size center:(CGPoint)center strokeWidth:(CGFloat)sw inflexionStyle:(PNLineChartPointStyle)type andColor:(UIColor *)color andAlpha:(CGFloat)alfa { + //Make the size a little bigger so it includes also border stroke + CGSize aSize = CGSizeMake(size + sw, size + sw); + + + UIGraphicsBeginImageContextWithOptions(aSize, NO, 0.0); + CGContextRef context = UIGraphicsGetCurrentContext(); + + + if (type == PNLineChartPointStyleCircle) { + CGContextAddArc(context, (size + sw) / 2, (size + sw) / 2, size / 2, 0, (CGFloat) (M_PI * 2), YES); + } else if (type == PNLineChartPointStyleSquare) { + CGContextAddRect(context, CGRectMake(sw / 2, sw / 2, size, size)); + } else if (type == PNLineChartPointStyleTriangle) { + CGContextMoveToPoint(context, sw / 2, size + sw / 2); + CGContextAddLineToPoint(context, size + sw / 2, size + sw / 2); + CGContextAddLineToPoint(context, size / 2 + sw / 2, sw / 2); + CGContextAddLineToPoint(context, sw / 2, size + sw / 2); + CGContextClosePath(context); + } + + //Set some stroke properties + CGContextSetLineWidth(context, sw); + CGContextSetAlpha(context, alfa); + CGContextSetStrokeColorWithColor(context, color.CGColor); + + //Finally draw + CGContextDrawPath(context, kCGPathStroke); + + //now get the image from the context + UIImage *squareImage = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + + //// Translate origin + CGFloat originX = (CGFloat) (center.x - (size + sw) / 2.0); + CGFloat originY = (CGFloat) (center.y - (size + sw) / 2.0); + + UIImageView *squareImageView = [[UIImageView alloc] initWithImage:squareImage]; + [squareImageView setFrame:CGRectMake(originX, originY, size + sw, size + sw)]; + return squareImageView; +} + +#pragma mark setter and getter + +- (CATextLayer *)createPointLabelFor:(CGFloat)grade pointCenter:(CGPoint)pointCenter width:(CGFloat)width withChartData:(PNLineChartData *)chartData { + CATextLayer *textLayer = [[CATextLayer alloc] init]; + [textLayer setAlignmentMode:kCAAlignmentCenter]; + [textLayer setForegroundColor:[chartData.pointLabelColor CGColor]]; + [textLayer setBackgroundColor:self.backgroundColor.CGColor]; +// [textLayer setBackgroundColor:[self.backgroundColor colorWithAlphaComponent:0.8].CGColor]; +// [textLayer setCornerRadius:(CGFloat) (textLayer.fontSize / 8.0)]; + + if (chartData.pointLabelFont != nil) { + [textLayer setFont:(__bridge CFTypeRef) (chartData.pointLabelFont)]; + textLayer.fontSize = [chartData.pointLabelFont pointSize]; + } + + CGFloat textHeight = (CGFloat) (textLayer.fontSize * 1.1); + // FIXME: convert the grade to string and use its length instead of hardcoding 8 + CGFloat textWidth = width * 8; + CGFloat textStartPosY; + + textStartPosY = pointCenter.y - textLayer.fontSize; + + [self.layer addSublayer:textLayer]; + + if (chartData.pointLabelFormat != nil) { + [textLayer setString:[[NSString alloc] initWithFormat:chartData.pointLabelFormat, grade]]; + } else { + [textLayer setString:[[NSString alloc] initWithFormat:_yLabelFormat, grade]]; + } + + [textLayer setFrame:CGRectMake(0, 0, textWidth, textHeight)]; + [textLayer setPosition:CGPointMake(pointCenter.x, textStartPosY)]; + textLayer.contentsScale = [UIScreen mainScreen].scale; + + return textLayer; +} + +- (CABasicAnimation *)fadeAnimation { + CABasicAnimation *fadeAnimation = nil; + if (self.displayAnimated) { + fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + fadeAnimation.fromValue = @0.0F; + fadeAnimation.toValue = @1.0F; + fadeAnimation.duration = 2.0; + } + return fadeAnimation; +} + +- (CABasicAnimation *)pathAnimation { + if (self.displayAnimated && !_pathAnimation) { + _pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + _pathAnimation.duration = 1.0; + _pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + _pathAnimation.fromValue = @0.0f; + _pathAnimation.toValue = @1.0f; + } + if(!self.displayAnimated) { + _pathAnimation = nil; + } + return _pathAnimation; +} + +@end diff --git a/PNChartdemo/PNChart/PNLineChartData.h b/PNChartdemo/PNChart/PNLineChartData.h new file mode 100755 index 0000000..2158e8e --- /dev/null +++ b/PNChartdemo/PNChart/PNLineChartData.h @@ -0,0 +1,61 @@ +// +// Created by J��rg Polakowski on 14/12/13. +// Copyright (c) 2013 kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +typedef NS_ENUM(NSUInteger, PNLineChartPointStyle) { + PNLineChartPointStyleNone = 0, + PNLineChartPointStyleCircle = 1, + PNLineChartPointStyleSquare = 3, + PNLineChartPointStyleTriangle = 4 +}; + +@class PNLineChartDataItem; + +typedef PNLineChartDataItem *(^LCLineChartDataGetter)(NSUInteger item); + +@interface PNLineChartColorRange : NSObject<NSCopying> + +@property(nonatomic) NSRange range; +@property(nonatomic) BOOL inclusive; +@property(nonatomic, retain) UIColor *color; + +- (id)initWithRange:(NSRange)range color:(UIColor *)color; + +@end + +@interface PNLineChartData : NSObject + +@property (strong) UIColor *color; +@property (nonatomic) CGFloat alpha; +@property NSUInteger itemCount; +@property (copy) LCLineChartDataGetter getData; +@property (strong, nonatomic) NSString *dataTitle; + +@property (nonatomic) BOOL showPointLabel; +@property (nonatomic) UIColor *pointLabelColor; +@property (nonatomic) UIFont *pointLabelFont; +@property (nonatomic) NSString *pointLabelFormat; + +@property (nonatomic, assign) PNLineChartPointStyle inflexionPointStyle; +@property (nonatomic) UIColor *inflexionPointColor; + +/** + * if rangeColor is set and the lineChartData values are within any + * of the given range then use the rangeColor.color otherwise use + * self.color for the rest of the graph + */ +@property(strong) NSArray<PNLineChartColorRange *> *rangeColors; + +/** + * If PNLineChartPointStyle is circle, this returns the circle's diameter. + * If PNLineChartPointStyle is square, each point is a square with each side equal in length to this value. + */ +@property (nonatomic, assign) CGFloat inflexionPointWidth; + +@property (nonatomic, assign) CGFloat lineWidth; + +@end diff --git a/PNChartdemo/PNChart/PNLineChartData.m b/PNChartdemo/PNChart/PNLineChartData.m new file mode 100755 index 0000000..bbf2647 --- /dev/null +++ b/PNChartdemo/PNChart/PNLineChartData.m @@ -0,0 +1,54 @@ +// +// Created by J��rg Polakowski on 14/12/13. +// Copyright (c) 2013 kevinzhow. All rights reserved. +// + +#import "PNLineChartData.h" + + +@implementation PNLineChartColorRange + +- (id)initWithRange:(NSRange)range color:(UIColor *)color { + self = [super init]; + if (self) { + self.range = range; + self.color = color; + } + return self; +} + + +- (id)copyWithZone:(NSZone *)zone { + PNLineChartColorRange *copy = [[self class] allocWithZone:zone]; + copy.color = self.color; + copy.range = self.range; + return copy; +} + +@end + +@implementation PNLineChartData + +- (id)init +{ + self = [super init]; + if (self) { + [self setupDefaultValues]; + } + + return self; +} + +- (void)setupDefaultValues +{ + _inflexionPointStyle = PNLineChartPointStyleNone; + _inflexionPointWidth = 6.f; + _lineWidth = 2.f; + _alpha = 1.f; + _showPointLabel = NO; + _pointLabelColor = [UIColor blackColor]; + _pointLabelFormat = @"%1.f"; + _rangeColors = nil; +} + +@end diff --git a/PNChartdemo/PNChart/PNLineChartDataItem.h b/PNChartdemo/PNChart/PNLineChartDataItem.h new file mode 100755 index 0000000..ad2d23d --- /dev/null +++ b/PNChartdemo/PNChart/PNLineChartDataItem.h @@ -0,0 +1,17 @@ +// +// Created by J��rg Polakowski on 14/12/13. +// Copyright (c) 2013 kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +@interface PNLineChartDataItem : NSObject + ++ (PNLineChartDataItem *)dataItemWithY:(CGFloat)y; ++ (PNLineChartDataItem *)dataItemWithY:(CGFloat)y andRawY:(CGFloat)rawY; + +@property (readonly) CGFloat y; // should be within the y range +@property (readonly) CGFloat rawY; // this is the raw value, used for point label. + +@end diff --git a/PNChartdemo/PNChart/PNLineChartDataItem.m b/PNChartdemo/PNChart/PNLineChartDataItem.m new file mode 100755 index 0000000..1beea91 --- /dev/null +++ b/PNChartdemo/PNChart/PNLineChartDataItem.m @@ -0,0 +1,38 @@ +// +// Created by J��rg Polakowski on 14/12/13. +// Copyright (c) 2013 kevinzhow. All rights reserved. +// + +#import "PNLineChartDataItem.h" + +@interface PNLineChartDataItem () + +- (id)initWithY:(CGFloat)y andRawY:(CGFloat)rawY; + +@property (readwrite) CGFloat y; // should be within the y range +@property (readwrite) CGFloat rawY; // this is the raw value, used for point label. + +@end + +@implementation PNLineChartDataItem + ++ (PNLineChartDataItem *)dataItemWithY:(CGFloat)y +{ + return [[PNLineChartDataItem alloc] initWithY:y andRawY:y]; +} + ++ (PNLineChartDataItem *)dataItemWithY:(CGFloat)y andRawY:(CGFloat)rawY { + return [[PNLineChartDataItem alloc] initWithY:y andRawY:rawY]; +} + +- (id)initWithY:(CGFloat)y andRawY:(CGFloat)rawY +{ + if ((self = [super init])) { + self.y = y; + self.rawY = rawY; + } + + return self; +} + +@end diff --git a/PNChartdemo/PNChart/PNPieChart.h b/PNChartdemo/PNChart/PNPieChart.h new file mode 100755 index 0000000..204afb5 --- /dev/null +++ b/PNChartdemo/PNChart/PNPieChart.h @@ -0,0 +1,68 @@ +// +// PNPieChart.h +// PNChartDemo +// +// Created by Hang Zhang on 14-5-5. +// Copyright (c) 2014��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "PNPieChartDataItem.h" +#import "PNGenericChart.h" +#import "PNChartDelegate.h" + +@interface PNPieChart : PNGenericChart + +- (id)initWithFrame:(CGRect)frame items:(NSArray *)items; + +@property (nonatomic, readonly) NSArray *items; + +/** Default is 18-point Avenir Medium. */ +@property (nonatomic) UIFont *descriptionTextFont; + +/** Default is white. */ +@property (nonatomic) UIColor *descriptionTextColor; + +/** Default is black, with an alpha of 0.4. */ +@property (nonatomic) UIColor *descriptionTextShadowColor; + +/** Default is CGSizeMake(0, 1). */ +@property (nonatomic) CGSize descriptionTextShadowOffset; + +/** Default is 1.0. */ +@property (nonatomic) NSTimeInterval duration; + +/** Show only values, this is useful when legend is present */ +@property (nonatomic) BOOL showOnlyValues; + +/** Show absolute values not relative i.e. percentages */ +@property (nonatomic) BOOL showAbsoluteValues; + +/** Hide percentage labels less than cutoff value */ +@property (nonatomic, assign) CGFloat labelPercentageCutoff; + +/** Default YES. */ +@property (nonatomic) BOOL shouldHighlightSectorOnTouch; + +/** Current outer radius. Override recompute() to change this. **/ +@property (nonatomic) CGFloat outerCircleRadius; + +/** Current inner radius. Override recompute() to change this. **/ +@property (nonatomic) CGFloat innerCircleRadius; + +@property (nonatomic, weak) id<PNChartDelegate> delegate; + +/** Update chart items. Does not update chart itself. */ +- (void)updateChartData:(NSArray *)data; + +/** Multiple selection */ +@property (nonatomic, assign) BOOL enableMultipleSelection; + +/** show only tiles, not values or percentage */ +@property (nonatomic) BOOL hideValues; + +- (void)strokeChart; + +- (void)recompute; + +@end diff --git a/PNChartdemo/PNChart/PNPieChart.m b/PNChartdemo/PNChart/PNPieChart.m new file mode 100755 index 0000000..ae9ab7a --- /dev/null +++ b/PNChartdemo/PNChart/PNPieChart.m @@ -0,0 +1,508 @@ +// +// PNPieChart.m +// PNChartDemo +// +// Created by Hang Zhang on 14-5-5. +// Copyright (c) 2014��� kevinzhow. All rights reserved. +// + +#import "PNPieChart.h" +//needed for the expected label size +#import "PNLineChart.h" + +@interface PNPieChart() + +@property (nonatomic) NSArray *items; +@property (nonatomic) NSArray *endPercentages; + +@property (nonatomic) UIView *contentView; +@property (nonatomic) CAShapeLayer *pieLayer; +@property (nonatomic) NSMutableArray *descriptionLabels; +@property (strong, nonatomic) CAShapeLayer *sectorHighlight; + +@property (nonatomic, strong) NSMutableDictionary *selectedItems; + +- (void)loadDefault; + +- (UILabel *)descriptionLabelForItemAtIndex:(NSUInteger)index; +- (PNPieChartDataItem *)dataItemForIndex:(NSUInteger)index; +- (CGFloat)startPercentageForItemAtIndex:(NSUInteger)index; +- (CGFloat)endPercentageForItemAtIndex:(NSUInteger)index; +- (CGFloat)ratioForItemAtIndex:(NSUInteger)index; + +- (CAShapeLayer *)newCircleLayerWithRadius:(CGFloat)radius + borderWidth:(CGFloat)borderWidth + fillColor:(UIColor *)fillColor + borderColor:(UIColor *)borderColor + startPercentage:(CGFloat)startPercentage + endPercentage:(CGFloat)endPercentage; + + +@end + + +@implementation PNPieChart + +-(id)initWithFrame:(CGRect)frame items:(NSArray *)items{ + self = [self initWithFrame:frame]; + if(self){ + _items = [NSArray arrayWithArray:items]; + [self baseInit]; + } + + return self; +} + +- (void)awakeFromNib{ + [super awakeFromNib]; + [self baseInit]; +} + +- (void)baseInit{ + _selectedItems = [NSMutableDictionary dictionary]; + //������������������,���������������������������������������������,������������������,���������������,���������������view���������,��������������������������������� + + CGFloat minimal = (CGRectGetWidth(self.bounds) < CGRectGetHeight(self.bounds)) ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds); + + _outerCircleRadius = minimal / 2; + _innerCircleRadius = minimal / 6; +// _outerCircleRadius = CGRectGetWidth(self.bounds) / 2; +// _innerCircleRadius = CGRectGetWidth(self.bounds) / 6; + _descriptionTextColor = [UIColor whiteColor]; + _descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:18.0]; + _descriptionTextShadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; + _descriptionTextShadowOffset = CGSizeMake(0, 1); + _duration = 1.0; + _shouldHighlightSectorOnTouch = YES; + _enableMultipleSelection = NO; + _hideValues = NO; + + [super setupDefaultValues]; + [self loadDefault]; +} + +- (void)loadDefault{ + __block CGFloat currentTotal = 0; + CGFloat total = [[self.items valueForKeyPath:@"@sum.value"] floatValue]; + NSMutableArray *endPercentages = [NSMutableArray new]; + [_items enumerateObjectsUsingBlock:^(PNPieChartDataItem *item, NSUInteger idx, BOOL *stop) { + if (total == 0){ + [endPercentages addObject:@(1.0 / _items.count * (idx + 1))]; + }else{ + currentTotal += item.value; + [endPercentages addObject:@(currentTotal / total)]; + } + }]; + self.endPercentages = [endPercentages copy]; + + [_contentView removeFromSuperview]; + _contentView = [[UIView alloc] initWithFrame:self.bounds]; + [self addSubview:_contentView]; + _descriptionLabels = [NSMutableArray new]; + + _pieLayer = [CAShapeLayer layer]; + [_contentView.layer addSublayer:_pieLayer]; + +} + +/** Override this to change how inner attributes are computed. **/ +- (void)recompute { + + //������ + CGFloat minimal = (CGRectGetWidth(self.bounds) < CGRectGetHeight(self.bounds)) ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds); + self.outerCircleRadius = minimal / 2; + self.innerCircleRadius = minimal / 6; +} + +#pragma mark - + +- (void)strokeChart{ + [self loadDefault]; + [self recompute]; + + PNPieChartDataItem *currentItem; + for (int i = 0; i < _items.count; i++) { + currentItem = [self dataItemForIndex:i]; + + + CGFloat startPercentage = [self startPercentageForItemAtIndex:i]; + CGFloat endPercentage = [self endPercentageForItemAtIndex:i]; + + CGFloat radius = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius) / 2; + CGFloat borderWidth = _outerCircleRadius - _innerCircleRadius; + + CAShapeLayer *currentPieLayer = [self newCircleLayerWithRadius:radius + borderWidth:borderWidth + fillColor:[UIColor clearColor] + borderColor:currentItem.color + startPercentage:startPercentage + endPercentage:endPercentage]; + [_pieLayer addSublayer:currentPieLayer]; + } + + [self maskChart]; + + for (int i = 0; i < _items.count; i++) { + UILabel *descriptionLabel = [self descriptionLabelForItemAtIndex:i]; + [_contentView addSubview:descriptionLabel]; + [_descriptionLabels addObject:descriptionLabel]; + } + + [self addAnimationIfNeeded]; +} + +- (UILabel *)descriptionLabelForItemAtIndex:(NSUInteger)index{ + PNPieChartDataItem *currentDataItem = [self dataItemForIndex:index]; + CGFloat distance = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius) / 2; + CGFloat centerPercentage = ([self startPercentageForItemAtIndex:index] + [self endPercentageForItemAtIndex:index])/ 2; + CGFloat rad = centerPercentage * 2 * M_PI; + + UILabel *descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 80)]; + NSString *titleText = currentDataItem.textDescription; + + NSString *titleValue; + + if (self.showAbsoluteValues) { + titleValue = [NSString stringWithFormat:@"%.0f",currentDataItem.value]; + }else{ + titleValue = [NSString stringWithFormat:@"%.0f%%",[self ratioForItemAtIndex:index] * 100]; + } + + if (self.hideValues) + descriptionLabel.text = titleText; + else if(!titleText || self.showOnlyValues) + descriptionLabel.text = titleValue; + else { + NSString* str = [titleValue stringByAppendingString:[NSString stringWithFormat:@"\n%@",titleText]]; + descriptionLabel.text = str ; + } + + //If value is less than cutoff, show no label + if ([self ratioForItemAtIndex:index] < self.labelPercentageCutoff ) + { + descriptionLabel.text = nil; + } + + CGPoint center = CGPointMake(_outerCircleRadius + distance * sin(rad), + _outerCircleRadius - distance * cos(rad)); + + descriptionLabel.font = _descriptionTextFont; + CGSize labelSize = [descriptionLabel.text sizeWithAttributes:@{NSFontAttributeName:descriptionLabel.font}]; + descriptionLabel.frame = CGRectMake(descriptionLabel.frame.origin.x, descriptionLabel.frame.origin.y, + descriptionLabel.frame.size.width, labelSize.height); + descriptionLabel.numberOfLines = 0; + descriptionLabel.textColor = _descriptionTextColor; + descriptionLabel.shadowColor = _descriptionTextShadowColor; + descriptionLabel.shadowOffset = _descriptionTextShadowOffset; + descriptionLabel.textAlignment = NSTextAlignmentCenter; + descriptionLabel.center = center; + descriptionLabel.alpha = 0; + descriptionLabel.backgroundColor = [UIColor clearColor]; + return descriptionLabel; +} + +- (void)updateChartData:(NSArray *)items { + self.items = items; +} + +- (PNPieChartDataItem *)dataItemForIndex:(NSUInteger)index{ + return self.items[index]; +} + +- (CGFloat)startPercentageForItemAtIndex:(NSUInteger)index{ + if(index == 0){ + return 0; + } + + return [_endPercentages[index - 1] floatValue]; +} + +- (CGFloat)endPercentageForItemAtIndex:(NSUInteger)index{ + return [_endPercentages[index] floatValue]; +} + +- (CGFloat)ratioForItemAtIndex:(NSUInteger)index{ + return [self endPercentageForItemAtIndex:index] - [self startPercentageForItemAtIndex:index]; +} + +#pragma mark private methods + +- (CAShapeLayer *)newCircleLayerWithRadius:(CGFloat)radius + borderWidth:(CGFloat)borderWidth + fillColor:(UIColor *)fillColor + borderColor:(UIColor *)borderColor + startPercentage:(CGFloat)startPercentage + endPercentage:(CGFloat)endPercentage{ + CAShapeLayer *circle = [CAShapeLayer layer]; + + CGPoint center = CGPointMake(CGRectGetMidX(self.bounds),CGRectGetMidY(self.bounds)); + + UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center + radius:radius + startAngle:-M_PI_2 + endAngle:M_PI_2 * 3 + clockwise:YES]; + + circle.fillColor = fillColor.CGColor; + circle.strokeColor = borderColor.CGColor; + circle.strokeStart = startPercentage; + circle.strokeEnd = endPercentage; + circle.lineWidth = borderWidth; + circle.path = path.CGPath; + + return circle; +} + +- (void)maskChart{ + CGFloat radius = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius) / 2; + CGFloat borderWidth = _outerCircleRadius - _innerCircleRadius; + CAShapeLayer *maskLayer = [self newCircleLayerWithRadius:radius + borderWidth:borderWidth + fillColor:[UIColor clearColor] + borderColor:[UIColor blackColor] + startPercentage:0 + endPercentage:1]; + + _pieLayer.mask = maskLayer; +} + +- (void)addAnimationIfNeeded{ + if (self.displayAnimated) { + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + animation.duration = _duration; + animation.fromValue = @0; + animation.toValue = @1; + animation.delegate = self; + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + animation.removedOnCompletion = YES; + [_pieLayer.mask addAnimation:animation forKey:@"circleAnimation"]; + } + else { + // Add description labels since no animation is required + [_descriptionLabels enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [obj setAlpha:1]; + }]; + } +} + +- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{ + [_descriptionLabels enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [UIView animateWithDuration:0.2 animations:^(){ + [obj setAlpha:1]; + }]; + }]; +} + +- (void)didTouchAt:(CGPoint)touchLocation +{ + CGPoint circleCenter = CGPointMake(_contentView.bounds.size.width/2, _contentView.bounds.size.height/2); + + CGFloat distanceFromCenter = sqrtf(powf((touchLocation.y - circleCenter.y),2) + powf((touchLocation.x - circleCenter.x),2)); + + if (distanceFromCenter < _innerCircleRadius) { + if ([self.delegate respondsToSelector:@selector(didUnselectPieItem)]) { + [self.delegate didUnselectPieItem]; + } + [self.sectorHighlight removeFromSuperlayer]; + return; + } + + CGFloat percentage = [self findPercentageOfAngleInCircle:circleCenter fromPoint:touchLocation]; + int index = 0; + while (percentage > [self endPercentageForItemAtIndex:index]) { + index ++; + } + + if ([self.delegate respondsToSelector:@selector(userClickedOnPieIndexItem:)]) { + [self.delegate userClickedOnPieIndexItem:index]; + } + + if (self.shouldHighlightSectorOnTouch) + { + if (!self.enableMultipleSelection) + { + if (self.sectorHighlight) + [self.sectorHighlight removeFromSuperlayer]; + } + + PNPieChartDataItem *currentItem = [self dataItemForIndex:index]; + + CGFloat red,green,blue,alpha; + UIColor *old = currentItem.color; + [old getRed:&red green:&green blue:&blue alpha:&alpha]; + alpha /= 2; + UIColor *newColor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; + + CGFloat startPercentage = [self startPercentageForItemAtIndex:index]; + CGFloat endPercentage = [self endPercentageForItemAtIndex:index]; + + self.sectorHighlight = [self newCircleLayerWithRadius:_outerCircleRadius + 5 + borderWidth:10 + fillColor:[UIColor clearColor] + borderColor:newColor + startPercentage:startPercentage + endPercentage:endPercentage]; + + if (self.enableMultipleSelection) + { + NSString *dictIndex = [NSString stringWithFormat:@"%d", index]; + CAShapeLayer *indexShape = [self.selectedItems valueForKey:dictIndex]; + if (indexShape) + { + [indexShape removeFromSuperlayer]; + [self.selectedItems removeObjectForKey:dictIndex]; + } + else + { + [self.selectedItems setObject:self.sectorHighlight forKey:dictIndex]; + [_contentView.layer addSublayer:self.sectorHighlight]; + } + } + else + { + [_contentView.layer addSublayer:self.sectorHighlight]; + } + } +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + for (UITouch *touch in touches) { + CGPoint touchLocation = [touch locationInView:_contentView]; + [self didTouchAt:touchLocation]; + } +} + +- (CGFloat) findPercentageOfAngleInCircle:(CGPoint)center fromPoint:(CGPoint)reference{ + //Find angle of line Passing In Reference And Center + CGFloat angleOfLine = atanf((reference.y - center.y) / (reference.x - center.x)); + CGFloat percentage = (angleOfLine + M_PI/2)/(2 * M_PI); + return (reference.x - center.x) > 0 ? percentage : percentage + .5; +} + +- (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth{ + if ([self.items count] < 1) { + return nil; + } + + /* This is a small circle that refers to the chart data */ + CGFloat legendCircle = 16; + + CGFloat hSpacing = 0; + + CGFloat beforeLabel = legendCircle + hSpacing; + + /* x and y are the coordinates of the starting point of each legend item */ + CGFloat x = 0; + CGFloat y = 0; + + /* accumulated width and height */ + CGFloat totalWidth = 0; + CGFloat totalHeight = 0; + + NSMutableArray *legendViews = [[NSMutableArray alloc] init]; + + /* Determine the max width of each legend item */ + CGFloat maxLabelWidth; + if (self.legendStyle == PNLegendItemStyleStacked) { + maxLabelWidth = mWidth - beforeLabel; + }else{ + maxLabelWidth = MAXFLOAT; + } + + /* this is used when labels wrap text and the line + * should be in the middle of the first row */ + CGFloat singleRowHeight = [PNLineChart sizeOfString:@"Test" + withWidth:MAXFLOAT + font:self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]].height; + + NSUInteger counter = 0; + NSUInteger rowWidth = 0; + NSUInteger rowMaxHeight = 0; + + for (PNPieChartDataItem *pdata in self.items) { + /* Expected label size*/ + CGSize labelsize = [PNLineChart sizeOfString:pdata.textDescription + withWidth:maxLabelWidth + font:self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]]; + + if ((rowWidth + labelsize.width + beforeLabel > mWidth)&&(self.legendStyle == PNLegendItemStyleSerial)) { + rowWidth = 0; + x = 0; + y += rowMaxHeight; + rowMaxHeight = 0; + } + rowWidth += labelsize.width + beforeLabel; + totalWidth = self.legendStyle == PNLegendItemStyleSerial ? fmaxf(rowWidth, totalWidth) : fmaxf(totalWidth, labelsize.width + beforeLabel); + // Add inflexion type + [legendViews addObject:[self drawInflexion:legendCircle * .6 + center:CGPointMake(x + legendCircle / 2, y + singleRowHeight / 2) + andColor:pdata.color]]; + + + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x + beforeLabel, y, labelsize.width, labelsize.height)]; + label.text = pdata.textDescription; + label.textColor = self.legendFontColor ? self.legendFontColor : [UIColor blackColor]; + label.font = self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]; + label.lineBreakMode = NSLineBreakByWordWrapping; + label.numberOfLines = 0; + + + rowMaxHeight = fmaxf(rowMaxHeight, labelsize.height); + x += self.legendStyle == PNLegendItemStyleStacked ? 0 : labelsize.width + beforeLabel; + y += self.legendStyle == PNLegendItemStyleStacked ? labelsize.height : 0; + + + totalHeight = self.legendStyle == PNLegendItemStyleSerial ? fmaxf(totalHeight, rowMaxHeight + y) : totalHeight + labelsize.height; + [legendViews addObject:label]; + counter ++; + } + + UIView *legend = [[UIView alloc] initWithFrame:CGRectMake(0, 0, totalWidth, totalHeight)]; + + for (UIView* v in legendViews) { + [legend addSubview:v]; + } + return legend; +} + + +- (UIImageView*)drawInflexion:(CGFloat)size center:(CGPoint)center andColor:(UIColor*)color +{ + //Make the size a little bigger so it includes also border stroke + CGSize aSize = CGSizeMake(size, size); + + + UIGraphicsBeginImageContextWithOptions(aSize, NO, 0.0); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextAddArc(context, size/2, size/ 2, size/2, 0, M_PI*2, YES); + + + //Set some fill color + CGContextSetFillColorWithColor(context, color.CGColor); + + //Finally draw + CGContextDrawPath(context, kCGPathFill); + + //now get the image from the context + UIImage *squareImage = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + + //// Translate origin + CGFloat originX = center.x - (size) / 2.0; + CGFloat originY = center.y - (size) / 2.0; + + UIImageView *squareImageView = [[UIImageView alloc]initWithImage:squareImage]; + [squareImageView setFrame:CGRectMake(originX, originY, size, size)]; + return squareImageView; +} + +/* Redraw the chart on autolayout */ +-(void)layoutSubviews { + [super layoutSubviews]; + [self strokeChart]; +} + +@end diff --git a/PNChartdemo/PNChart/PNPieChartDataItem.h b/PNChartdemo/PNChart/PNPieChartDataItem.h new file mode 100755 index 0000000..08d48e9 --- /dev/null +++ b/PNChartdemo/PNChart/PNPieChartDataItem.h @@ -0,0 +1,25 @@ +// +// PNPieChartDataItem.h +// PNChartDemo +// +// Created by Hang Zhang on 14-5-5. +// Copyright (c) 2014��� kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +@interface PNPieChartDataItem : NSObject + ++ (instancetype)dataItemWithValue:(CGFloat)value + color:(UIColor*)color; + ++ (instancetype)dataItemWithValue:(CGFloat)value + color:(UIColor*)color + description:(NSString *)description; + +@property (nonatomic) CGFloat value; +@property (nonatomic) UIColor *color; +@property (nonatomic) NSString *textDescription; + +@end diff --git a/PNChartdemo/PNChart/PNPieChartDataItem.m b/PNChartdemo/PNChart/PNPieChartDataItem.m new file mode 100755 index 0000000..4bda818 --- /dev/null +++ b/PNChartdemo/PNChart/PNPieChartDataItem.m @@ -0,0 +1,38 @@ +// +// PNPieChartDataItem.m +// PNChartDemo +// +// Created by Hang Zhang on 14-5-5. +// Copyright (c) 2014��� kevinzhow. All rights reserved. +// + +#import "PNPieChartDataItem.h" +#import <UIKit/UIKit.h> + +@implementation PNPieChartDataItem + + ++ (instancetype)dataItemWithValue:(CGFloat)value + color:(UIColor*)color{ + PNPieChartDataItem *item = [PNPieChartDataItem new]; + item.value = value; + item.color = color; + return item; +} + ++ (instancetype)dataItemWithValue:(CGFloat)value + color:(UIColor*)color + description:(NSString *)description { + PNPieChartDataItem *item = [PNPieChartDataItem dataItemWithValue:value color:color]; + item.textDescription = description; + return item; +} + +- (void)setValue:(CGFloat)value{ + NSAssert(value >= 0, @"value should >= 0"); + if (value != _value){ + _value = value; + } +} + +@end diff --git a/PNChartdemo/PNChart/PNRadarChart.h b/PNChartdemo/PNChart/PNRadarChart.h new file mode 100755 index 0000000..87871d0 --- /dev/null +++ b/PNChartdemo/PNChart/PNRadarChart.h @@ -0,0 +1,52 @@ +// +// PNRadarChart.h +// PNChartDemo +// +// Created by Lei on 15/7/1. +// Copyright (c) 2015��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "PNGenericChart.h" +#import "PNRadarChartDataItem.h" + +#define MAXCIRCLE 20 + +typedef NS_ENUM(NSUInteger, PNRadarChartLabelStyle) { + PNRadarChartLabelStyleCircle = 0, + PNRadarChartLabelStyleHorizontal, + PNRadarChartLabelStyleHidden, +}; + +@interface PNRadarChart : PNGenericChart + +-(id)initWithFrame:(CGRect)frame items:(NSArray *)items valueDivider:(CGFloat)unitValue; +/** + *Draws the chart in an animated fashion. + */ +-(void)strokeChart; + +/** Array of `RadarChartDataItem` objects, one for each corner. */ +@property (nonatomic) NSArray *chartData; +/** The unit of this chart ,default is 1 */ +@property (nonatomic) CGFloat valueDivider; +/** The maximum for the range of values to display on the chart */ +@property (nonatomic) CGFloat maxValue; +/** Default is gray. */ +@property (nonatomic) UIColor *webColor; +/** Default is green , with an alpha of 0.7 */ +@property (nonatomic) UIColor *plotColor; +/** Default is black */ +@property (nonatomic) UIColor *fontColor; +/** Default is orange */ +@property (nonatomic) UIColor *graduationColor; +/** Default is 15 */ +@property (nonatomic) CGFloat fontSize; +/** Controls the labels display style that around chart */ +@property (nonatomic, assign) PNRadarChartLabelStyle labelStyle; +/** Tap the label will display detail value ,default is YES. */ +@property (nonatomic, assign) BOOL isLabelTouchable; +/** is show graduation on the chart ,default is NO. */ +@property (nonatomic, assign) BOOL isShowGraduation; + +@end diff --git a/PNChartdemo/PNChart/PNRadarChart.m b/PNChartdemo/PNChart/PNRadarChart.m new file mode 100755 index 0000000..9dace2b --- /dev/null +++ b/PNChartdemo/PNChart/PNRadarChart.m @@ -0,0 +1,373 @@ +// +// PNRadarChart.m +// PNChartDemo +// +// Created by Lei on 15/7/1. +// Copyright (c) 2015��� kevinzhow. All rights reserved. +// + +#import "PNRadarChart.h" + +@interface PNRadarChart() + +@property (nonatomic) CGFloat centerX; +@property (nonatomic) CGFloat centerY; +@property (nonatomic) NSMutableArray *pointsToWebArrayArray; +@property (nonatomic) NSMutableArray *pointsToPlotArray; +@property (nonatomic) UILabel *detailLabel; +@property (nonatomic) CGFloat lengthUnit; +@property (nonatomic) CAShapeLayer *chartPlot; + +@end + + +@implementation PNRadarChart + +- (id)initWithFrame:(CGRect)frame items:(NSArray *)items valueDivider:(CGFloat)unitValue { + self=[super initWithFrame:frame]; + if (self) { + self.backgroundColor = [UIColor clearColor]; + self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + + //Public iVar + if ([items count]< 3)//At least three corners of A polygon ,If the count of items is less than 3 will add 3 default values + { + NSLog( @"At least three items!"); + NSArray *defaultArray = @[[PNRadarChartDataItem dataItemWithValue:0 description:@"Default"], + [PNRadarChartDataItem dataItemWithValue:0 description:@"Default"], + [PNRadarChartDataItem dataItemWithValue:0 description:@"Default"], + ]; + defaultArray = [defaultArray arrayByAddingObjectsFromArray:items]; + _chartData = [NSArray arrayWithArray:defaultArray]; + }else{ + _chartData = [NSArray arrayWithArray:items]; + } + _valueDivider = unitValue; + _maxValue = 1; + _webColor = [UIColor grayColor]; + _plotColor = [UIColor colorWithRed:.4 green:.8 blue:.4 alpha:.7]; + _fontColor = [UIColor blackColor]; + _graduationColor = [UIColor orangeColor]; + _fontSize = 15; + _labelStyle = PNRadarChartLabelStyleHorizontal; + _isLabelTouchable = YES; + _isShowGraduation = NO; + + //Private iVar + _centerX = frame.size.width/2; + _centerY = frame.size.height/2; + _pointsToWebArrayArray = [NSMutableArray array]; + _pointsToPlotArray = [NSMutableArray array]; + _lengthUnit = 0; + _chartPlot = [CAShapeLayer layer]; + _chartPlot.lineCap = kCALineCapButt; + _chartPlot.lineWidth = 1.0; + [self.layer addSublayer:_chartPlot]; + + [super setupDefaultValues]; + //init detailLabel + _detailLabel = [[UILabel alloc] init]; + _detailLabel.backgroundColor = [UIColor colorWithRed:.9 green:.9 blue:.1 alpha:.9]; + _detailLabel.textAlignment = NSTextAlignmentCenter; + _detailLabel.textColor = [UIColor colorWithWhite:1 alpha:1]; + _detailLabel.font = [UIFont systemFontOfSize:15]; + [_detailLabel setHidden:YES]; + [self addSubview:_detailLabel]; + + [self strokeChart]; + } + return self; +} + +#pragma mark - main +- (void)calculateChartPoints { + [_pointsToPlotArray removeAllObjects]; + [_pointsToWebArrayArray removeAllObjects]; + + //init Descriptions , Values and Angles. + NSMutableArray *descriptions = [NSMutableArray array]; + NSMutableArray *values = [NSMutableArray array]; + NSMutableArray *angles = [NSMutableArray array]; + for (int i=0;i<_chartData.count;i++) { + PNRadarChartDataItem *item = (PNRadarChartDataItem *)[_chartData objectAtIndex:i]; + [descriptions addObject:item.textDescription]; + [values addObject:[NSNumber numberWithFloat:item.value]]; + CGFloat angleValue = (float)i/(float)[_chartData count]*2*M_PI; + [angles addObject:[NSNumber numberWithFloat:angleValue]]; + } + + //calculate all the lengths + _maxValue = [self getMaxValueFromArray:values]; + CGFloat margin = 0; + if (_labelStyle==PNRadarChartLabelStyleCircle) { + margin = MIN(_centerX , _centerY)*3/10; + }else if (_labelStyle==PNRadarChartLabelStyleHorizontal) { + margin = [self getMaxWidthLabelFromArray:descriptions withFontSize:_fontSize]; + } + CGFloat maxLength = ceil(MIN(_centerX, _centerY) - margin); + int plotCircles = (_maxValue/_valueDivider); + if (plotCircles > MAXCIRCLE) { + NSLog(@"Circle number is higher than max"); + plotCircles = MAXCIRCLE; + _valueDivider = _maxValue/plotCircles; + } + _lengthUnit = maxLength/plotCircles; + NSArray *lengthArray = [self getLengthArrayWithCircleNum:(int)plotCircles]; + + //get all the points and plot + for (NSNumber *lengthNumber in lengthArray) { + CGFloat length = [lengthNumber floatValue]; + [_pointsToWebArrayArray addObject:[self getWebPointWithLength:length angleArray:angles]]; + } + int section = 0; + for (id value in values) { + CGFloat valueFloat = [value floatValue]; + if (valueFloat>_maxValue) { + NSString *reason = [NSString stringWithFormat:@"Value number is higher than max -value: %f - maxValue: %f",valueFloat,_maxValue]; + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil]; + return; + } + + CGFloat length = valueFloat/_maxValue*maxLength; + CGFloat angle = [[angles objectAtIndex:section] floatValue]; + CGFloat x = _centerX +length*cos(angle); + CGFloat y = _centerY +length*sin(angle); + NSValue* point = [NSValue valueWithCGPoint:CGPointMake(x, y)]; + [_pointsToPlotArray addObject:point]; + section++; + } + //set the labels + [self drawLabelWithMaxLength:maxLength labelArray:descriptions angleArray:angles]; + + } +#pragma mark - Draw + +- (void)drawRect:(CGRect)rect { + // Drawing backgound + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextClearRect(context, rect); + int section = 0; + //circles + for(NSArray *pointArray in _pointsToWebArrayArray){ + //plot backgound + CGContextRef graphContext = UIGraphicsGetCurrentContext(); + CGContextBeginPath(graphContext); + CGPoint beginPoint = [[pointArray objectAtIndex:0] CGPointValue]; + CGContextMoveToPoint(graphContext, beginPoint.x, beginPoint.y); + for(NSValue* pointValue in pointArray){ + CGPoint point = [pointValue CGPointValue]; + CGContextAddLineToPoint(graphContext, point.x, point.y); + } + CGContextAddLineToPoint(graphContext, beginPoint.x, beginPoint.y); + CGContextSetStrokeColorWithColor(graphContext, _webColor.CGColor); + CGContextStrokePath(graphContext); + + } + //cuts + NSArray *largestPointArray = [_pointsToWebArrayArray lastObject]; + for (NSValue *pointValue in largestPointArray){ + section++; + if (section==1&&_isShowGraduation)continue; + + CGContextRef graphContext = UIGraphicsGetCurrentContext(); + CGContextBeginPath(graphContext); + CGContextMoveToPoint(graphContext, _centerX, _centerY); + CGPoint point = [pointValue CGPointValue]; + CGContextAddLineToPoint(graphContext, point.x, point.y); + CGContextSetStrokeColorWithColor(graphContext, _webColor.CGColor); + CGContextStrokePath(graphContext); + } + + +} + +- (void)strokeChart { + + [self calculateChartPoints]; + [self setNeedsDisplay]; + [_detailLabel setHidden:YES]; + + //Draw plot + [_chartPlot removeAllAnimations]; + UIBezierPath *plotline = [UIBezierPath bezierPath]; + CGPoint beginPoint = [[_pointsToPlotArray objectAtIndex:0] CGPointValue]; + [plotline moveToPoint:CGPointMake(beginPoint.x, beginPoint.y)]; + for(NSValue *pointValue in _pointsToPlotArray){ + CGPoint point = [pointValue CGPointValue]; + [plotline addLineToPoint:CGPointMake(point.x ,point.y)]; + + } + [plotline setLineWidth:1]; + [plotline setLineCapStyle:kCGLineCapButt]; + + _chartPlot.path = plotline.CGPath; + + _chartPlot.fillColor = _plotColor.CGColor; + + [self addAnimationIfNeeded]; + [self showGraduation]; +} + +#pragma mark - Helper + +- (void)drawLabelWithMaxLength:(CGFloat)maxLength labelArray:(NSArray *)labelArray angleArray:(NSArray *)angleArray { + //set labels + int labelTag = 121; + while (true) { + UIView *label = [self viewWithTag:labelTag]; + if(!label)break; + [label removeFromSuperview]; + } + int section = 0; + CGFloat labelLength = maxLength + maxLength/10; + + for (NSString *labelString in labelArray) { + CGFloat angle = [[angleArray objectAtIndex:section] floatValue]; + CGFloat x = _centerX + labelLength *cos(angle); + CGFloat y = _centerY + labelLength *sin(angle); + + UILabel *label = [[UILabel alloc] init] ; + label.backgroundColor = [UIColor clearColor]; + label.font = [UIFont systemFontOfSize:_fontSize]; + label.text = labelString; + label.tag = labelTag; + CGSize detailSize = [labelString sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:_fontSize]}]; + + switch (_labelStyle) { + case PNRadarChartLabelStyleCircle: + label.frame = CGRectMake(x-5*_fontSize/2, y-_fontSize/2, 5*_fontSize, _fontSize); + label.transform = CGAffineTransformMakeRotation(((float)section/[labelArray count])*(2*M_PI)+M_PI_2); + label.textAlignment = NSTextAlignmentCenter; + + break; + case PNRadarChartLabelStyleHorizontal: + if (x<_centerX) { + label.frame = CGRectMake(x-detailSize.width, y-detailSize.height/2, detailSize.width, detailSize.height); + label.textAlignment = NSTextAlignmentRight; + }else{ + label.frame = CGRectMake(x, y-detailSize.height/2, detailSize.width , detailSize.height); + label.textAlignment = NSTextAlignmentLeft; + } + break; + case PNRadarChartLabelStyleHidden: + [label setHidden:YES]; + break; + default: + break; + } + [label sizeToFit]; + + label.userInteractionEnabled = YES; + UITapGestureRecognizer *tapLabelGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapLabel:)]; + [label addGestureRecognizer:tapLabelGesture]; + [self addSubview:label]; + + section ++; + } + +} + +- (void)tapLabel:(UITapGestureRecognizer *)recognizer { + UILabel *label=(UILabel*)recognizer.view; + _detailLabel.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y-30, 50, 25); + for (PNRadarChartDataItem *item in _chartData) { + if ([label.text isEqualToString:item.textDescription]) { + _detailLabel.text = [NSString stringWithFormat:@"%.2f", item.value]; + break; + } + } + [_detailLabel setHidden:NO]; + +} + +- (void)showGraduation { + int labelTag = 112; + while (true) { + UIView *label = [self viewWithTag:labelTag]; + if(!label)break; + [label removeFromSuperview]; + } + int section = 0; + for (NSArray *pointsArray in _pointsToWebArrayArray) { + section++; + CGPoint labelPoint = [[pointsArray objectAtIndex:0] CGPointValue]; + UILabel *graduationLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelPoint.x-_lengthUnit, labelPoint.y-_lengthUnit*5/8, _lengthUnit*5/8, _lengthUnit)]; + graduationLabel.adjustsFontSizeToFitWidth = YES; + graduationLabel.tag = labelTag; + graduationLabel.font = [UIFont systemFontOfSize:ceil(_lengthUnit)]; + graduationLabel.textColor = [UIColor orangeColor]; + graduationLabel.text = [NSString stringWithFormat:@"%.0f",_valueDivider*section]; + [self addSubview:graduationLabel]; + if (_isShowGraduation) { + [graduationLabel setHidden:NO]; + }else{ + [graduationLabel setHidden:YES];} + } + +} + +- (NSArray *)getWebPointWithLength:(CGFloat)length angleArray:(NSArray *)angleArray { + NSMutableArray *pointArray = [NSMutableArray array]; + for (NSNumber *angleNumber in angleArray) { + CGFloat angle = [angleNumber floatValue]; + CGFloat x = _centerX + length*cos(angle); + CGFloat y = _centerY + length*sin(angle); + [pointArray addObject:[NSValue valueWithCGPoint:CGPointMake(x,y)]]; + } + return pointArray; + +} + +- (NSArray *)getLengthArrayWithCircleNum:(int)plotCircles { + NSMutableArray *lengthArray = [NSMutableArray array]; + CGFloat length = 0; + for (int i = 0; i < plotCircles; i++) { + length += _lengthUnit; + [lengthArray addObject:[NSNumber numberWithFloat:length]]; + } + return lengthArray; +} + +- (CGFloat)getMaxWidthLabelFromArray:(NSArray *)keyArray withFontSize:(CGFloat)size { + CGFloat maxWidth = 0; + for (NSString *str in keyArray) { + CGSize detailSize = [str sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:_fontSize]}]; + maxWidth = MAX(maxWidth, detailSize.width); + } + return maxWidth; +} + +- (CGFloat)getMaxValueFromArray:(NSArray *)valueArray { + CGFloat max = _maxValue; + for (NSNumber *valueNum in valueArray) { + CGFloat valueFloat = [valueNum floatValue]; + max = MAX(valueFloat, max); + } + return ceil(max); +} + +- (void)addAnimationIfNeeded +{ + if (self.displayAnimated) { + CABasicAnimation *animateScale = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; + animateScale.fromValue = [NSNumber numberWithFloat:0.f]; + animateScale.toValue = [NSNumber numberWithFloat:1.0f]; + + CABasicAnimation *animateMove = [CABasicAnimation animationWithKeyPath:@"position"]; + animateMove.fromValue = [NSValue valueWithCGPoint:CGPointMake(_centerX, _centerY)]; + animateMove.toValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)]; + + CABasicAnimation *animateAlpha = [CABasicAnimation animationWithKeyPath:@"opacity"]; + animateAlpha.fromValue = [NSNumber numberWithFloat:0.f]; + + CAAnimationGroup *aniGroup = [CAAnimationGroup animation]; + aniGroup.duration = 1.f; + aniGroup.repeatCount = 1; + aniGroup.animations = [NSArray arrayWithObjects:animateScale,animateMove,animateAlpha, nil]; + aniGroup.removedOnCompletion = YES; + + [_chartPlot addAnimation:aniGroup forKey:nil]; + } +} + +@end diff --git a/PNChartdemo/PNChart/PNRadarChartDataItem.h b/PNChartdemo/PNChart/PNRadarChartDataItem.h new file mode 100755 index 0000000..e67429c --- /dev/null +++ b/PNChartdemo/PNChart/PNRadarChartDataItem.h @@ -0,0 +1,19 @@ +// +// PNRadarChartDataItem.h +// PNChartDemo +// +// Created by Lei on 15/7/1. +// Copyright (c) 2015��� kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> + +@interface PNRadarChartDataItem : NSObject + ++ (instancetype)dataItemWithValue:(CGFloat)value + description:(NSString *)description; + +@property (nonatomic) CGFloat value; +@property (nonatomic,copy) NSString *textDescription; + +@end diff --git a/PNChartdemo/PNChart/PNRadarChartDataItem.m b/PNChartdemo/PNChart/PNRadarChartDataItem.m new file mode 100755 index 0000000..218ef6e --- /dev/null +++ b/PNChartdemo/PNChart/PNRadarChartDataItem.m @@ -0,0 +1,29 @@ +// +// PNRadarChartDataItem.m +// PNChartDemo +// +// Created by Lei on 15/7/1. +// Copyright (c) 2015��� kevinzhow. All rights reserved. +// + +#import "PNRadarChartDataItem.h" + +@implementation PNRadarChartDataItem + ++ (instancetype)dataItemWithValue:(CGFloat)value + description:(NSString *)description { + PNRadarChartDataItem *item = [PNRadarChartDataItem new]; + item.value = value; + item.textDescription = description; + return item; +} + +- (void)setValue:(CGFloat)value { + if (value < 0) { + value = 0; + NSLog(@"Value value can not be negative"); + } + _value = value; +} + +@end diff --git a/PNChartdemo/PNChart/PNScatterChart.h b/PNChartdemo/PNChart/PNScatterChart.h new file mode 100755 index 0000000..aa4eab8 --- /dev/null +++ b/PNChartdemo/PNChart/PNScatterChart.h @@ -0,0 +1,69 @@ +// +// PNScatterChart.h +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import <QuartzCore/QuartzCore.h> +#import "PNChartDelegate.h" +#import "PNGenericChart.h" +#import "PNScatterChartData.h" +#import "PNScatterChartDataItem.h" + +@interface PNScatterChart : PNGenericChart + +@property (nonatomic, retain) id<PNChartDelegate> delegate; + +/** Array of `ScatterChartData` objects, one for each line. */ +@property (nonatomic) NSArray *chartData; + +/** Controls whether to show the coordinate axis. Default is NO. */ +@property (nonatomic, getter = isShowCoordinateAxis) BOOL showCoordinateAxis; +@property (nonatomic) UIColor *axisColor; +@property (nonatomic) CGFloat axisWidth; + +/** String formatter for float values in x-axis/y-axis labels. If not set, defaults to @"%1.f" */ +@property (nonatomic, strong) NSString *xLabelFormat; +@property (nonatomic, strong) NSString *yLabelFormat; + +/** Default is true. */ +@property (nonatomic) BOOL showLabel; + +/** Default is 18-point Avenir Medium. */ +@property (nonatomic) UIFont *descriptionTextFont; + +/** Default is white. */ +@property (nonatomic) UIColor *descriptionTextColor; + +/** Default is black, with an alpha of 0.4. */ +@property (nonatomic) UIColor *descriptionTextShadowColor; + +/** Default is CGSizeMake(0, 1). */ +@property (nonatomic) CGSize descriptionTextShadowOffset; + +/** Default is 1.0. */ +@property (nonatomic) NSTimeInterval duration; + +@property (nonatomic) CGFloat AxisX_minValue; +@property (nonatomic) CGFloat AxisX_maxValue; + +@property (nonatomic) CGFloat AxisY_minValue; +@property (nonatomic) CGFloat AxisY_maxValue; + +- (void) setAxisXWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks; +- (void) setAxisYWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks; +- (void) setAxisXLabel:(NSArray *)array; +- (void) setAxisYLabel:(NSArray *)array; +- (void) setup; +- (void) drawLineFromPoint : (CGPoint) startPoint ToPoint : (CGPoint) endPoint WithLineWith : (CGFloat) lineWidth AndWithColor : (UIColor*) color; + +/** + * Update Chart Value + */ + +- (void)updateChartData:(NSArray *)data; + +@end diff --git a/PNChartdemo/PNChart/PNScatterChart.m b/PNChartdemo/PNChart/PNScatterChart.m new file mode 100755 index 0000000..a16cf85 --- /dev/null +++ b/PNChartdemo/PNChart/PNScatterChart.m @@ -0,0 +1,445 @@ +// +// PNScatterChart.m +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import "PNScatterChart.h" +#import "PNColor.h" +#import "PNChartLabel.h" +#import "PNScatterChartData.h" +#import "PNScatterChartDataItem.h" + +@interface PNScatterChart () + +@property (nonatomic, weak) CAShapeLayer *pathLayer; +@property (nonatomic, weak) NSMutableArray *verticalLineLayer; +@property (nonatomic, weak) NSMutableArray *horizentalLinepathLayer; + +@property (nonatomic) CGPoint startPoint; + +@property (nonatomic) CGPoint startPointVectorX; +@property (nonatomic) CGPoint endPointVecotrX; + +@property (nonatomic) CGPoint startPointVectorY; +@property (nonatomic) CGPoint endPointVecotrY; + +@property (nonatomic) CGFloat vectorX_Steps; +@property (nonatomic) CGFloat vectorY_Steps; + +@property (nonatomic) CGFloat vectorX_Size; +@property (nonatomic) CGFloat vectorY_Size; + +@property (nonatomic) NSMutableArray *axisX_labels; +@property (nonatomic) NSMutableArray *axisY_labels; + +@property (nonatomic) int AxisX_partNumber ; +@property (nonatomic) int AxisY_partNumber ; + +@property (nonatomic) CGFloat AxisX_step ; +@property (nonatomic) CGFloat AxisY_step ; + +@property (nonatomic) CGFloat AxisX_Margin; +@property (nonatomic) CGFloat AxisY_Margin; + +@property (nonatomic) BOOL isForUpdate; + +@end + + +@implementation PNScatterChart + +#pragma mark initialization + +- (id)initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder:coder]; + + if (self) { + [self setupDefaultValues]; + } + return self; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + [self setupDefaultValues]; + } + return self; +} + +- (void) setup +{ + [self vectorXSetup]; + [self vectorYSetup]; +} + +- (void)setupDefaultValues +{ + [super setupDefaultValues]; + + // Initialization code + self.backgroundColor = [UIColor whiteColor]; + self.clipsToBounds = YES; + _showLabel = YES; + _isForUpdate = NO; + self.userInteractionEnabled = YES; + + // Coordinate Axis Default Values + _showCoordinateAxis = YES; + _axisColor = [UIColor colorWithRed:0.4f green:0.4f blue:0.4f alpha:1.f]; + _axisWidth = 1.f; + + // Initialization code + _AxisX_Margin = 30 ; + _AxisY_Margin = 30 ; + +// self.frame = CGRectMake((SCREEN_WIDTH - self.frame.size.width) / 2, 200, self.frame.size.width, self.frame.size.height) ; + self.backgroundColor = [UIColor clearColor]; + + _startPoint.y = self.frame.size.height - self.AxisY_Margin ; + _startPoint.x = self.AxisX_Margin ; + + _axisX_labels = [NSMutableArray array]; + _axisY_labels = [NSMutableArray array]; + + _descriptionTextColor = [UIColor blackColor]; + _descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:9.0]; + _descriptionTextShadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; + _descriptionTextShadowOffset = CGSizeMake(0, 1); + _duration = 1.0; + +} + +#pragma mark calculating axis + +- (void) setAxisXWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks +{ + _AxisX_minValue = minVal ; + _AxisX_maxValue = maxVal ; + _AxisX_partNumber = numberOfTicks - 1; + _AxisX_step = (float)((maxVal - minVal)/_AxisX_partNumber); + + NSString *LabelFormat = self.xLabelFormat ? : @"%1.f"; + CGFloat tempValue = minVal ; + UILabel *label = [[UILabel alloc] init]; + label.text = [NSString stringWithFormat:LabelFormat,minVal] ; + [_axisX_labels addObject:label]; + for (int i = 0 ; i < _AxisX_partNumber; i++) { + tempValue = tempValue + _AxisX_step; + UILabel *tempLabel = [[UILabel alloc] init]; + tempLabel.text = [NSString stringWithFormat:LabelFormat,tempValue] ; + [_axisX_labels addObject:tempLabel]; + } +} + +- (void) setAxisYWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks +{ + _AxisY_minValue = minVal ; + _AxisY_maxValue = maxVal ; + _AxisY_partNumber = numberOfTicks - 1; + _AxisY_step = (float)((maxVal - minVal)/_AxisY_partNumber); + + NSString *LabelFormat = self.yLabelFormat ? : @"%1.f"; + CGFloat tempValue = minVal ; + UILabel *label = [[UILabel alloc] init]; + label.text = [NSString stringWithFormat:LabelFormat,minVal] ; + [_axisY_labels addObject:label]; + for (int i = 0 ; i < _AxisY_partNumber; i++) { + tempValue = tempValue + _AxisY_step; + UILabel *tempLabel = [[UILabel alloc] init]; + tempLabel.text = [NSString stringWithFormat:LabelFormat,tempValue] ; + [_axisY_labels addObject:tempLabel]; + } +} + +- (NSArray*) getAxisMinMax:(NSArray*)xValues +{ + float min = [xValues[0] floatValue]; + float max = [xValues[0] floatValue]; + for (NSNumber *number in xValues) + { + if ([number floatValue] > max) + max = [number floatValue]; + + if ([number floatValue] < min) + min = [number floatValue]; + } + NSArray *result = @[[NSNumber numberWithFloat:min], [NSNumber numberWithFloat:max]]; + + + return result; +} + +- (void)setAxisXLabel:(NSArray *)array { + if(array.count == ++_AxisX_partNumber){ + [_axisX_labels removeAllObjects]; + for(int i=0;i<array.count;i++){ + UILabel *label = [[UILabel alloc] init]; + label.text = [array objectAtIndex:i]; + [_axisX_labels addObject:label]; + } + } +} + +- (void)setAxisYLabel:(NSArray *)array { + if(array.count == ++_AxisY_partNumber){ + [_axisY_labels removeAllObjects]; + for(int i=0;i<array.count;i++){ + UILabel *label = [[UILabel alloc] init]; + label.text = [array objectAtIndex:i]; + [_axisY_labels addObject:label]; + } + } +} + +- (void) vectorXSetup +{ + _AxisX_partNumber += 1; + _vectorX_Size = self.frame.size.width - (_AxisX_Margin) - 15 ; + _vectorX_Steps = (_vectorX_Size) / (_AxisX_partNumber) ; + _endPointVecotrX = CGPointMake(_startPoint.x + _vectorX_Size, _startPoint.y) ; + _startPointVectorX = _startPoint ; +} + +- (void) vectorYSetup +{ + _AxisY_partNumber += 1; + _vectorY_Size = self.frame.size.height - (_AxisY_Margin) - 15; + _vectorY_Steps = (_vectorY_Size) / (_AxisY_partNumber); + _endPointVecotrY = CGPointMake(_startPoint.x, _startPoint.y - _vectorY_Size) ; + _startPointVectorY = _startPoint ; +} + +- (void) showXLabel : (UILabel *) descriptionLabel InPosition : (CGPoint) point +{ + CGRect frame = CGRectMake(point.x, point.y, 30, 10); + descriptionLabel.frame = frame; + descriptionLabel.font = _descriptionTextFont; + descriptionLabel.textColor = _descriptionTextColor; + descriptionLabel.shadowColor = _descriptionTextShadowColor; + descriptionLabel.shadowOffset = _descriptionTextShadowOffset; + descriptionLabel.textAlignment = NSTextAlignmentCenter; + descriptionLabel.backgroundColor = [UIColor clearColor]; + [self addSubview:descriptionLabel]; +} + +- (void)setChartData:(NSArray *)data +{ + __block CGFloat yFinilizeValue , xFinilizeValue; + __block CGFloat yValue , xValue; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + if (self.displayAnimated) { + [NSThread sleepForTimeInterval:1]; + } + // update UI on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + for (PNScatterChartData *chartData in data) { + for (NSUInteger i = 0; i < chartData.itemCount; i++) { + yValue = chartData.getData(i).y; + xValue = chartData.getData(i).x; + if (!(xValue >= _AxisX_minValue && xValue <= _AxisX_maxValue) || !(yValue >= _AxisY_minValue && yValue <= _AxisY_maxValue)) { + NSLog(@"input is not in correct range."); + exit(0); + } + xFinilizeValue = [self mappingIsForAxisX:true WithValue:xValue]; + yFinilizeValue = [self mappingIsForAxisX:false WithValue:yValue]; + CAShapeLayer *shape = [self drawingPointsForChartData:chartData AndWithX:xFinilizeValue AndWithY:yFinilizeValue]; + self.pathLayer = shape ; + [self.layer addSublayer:self.pathLayer]; + + [self addAnimationIfNeeded]; + } + } + }); + }); +} + +- (void)addAnimationIfNeeded{ + + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + pathAnimation.duration = _duration; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @(0.0f); + pathAnimation.toValue = @(1.0f); + pathAnimation.fillMode = kCAFillModeForwards; + self.layer.opacity = 1; + [self.pathLayer addAnimation:pathAnimation forKey:@"fade"]; + } +} + +- (CGFloat) mappingIsForAxisX : (BOOL) isForAxisX WithValue : (CGFloat) value{ + + if (isForAxisX) { + float temp = _startPointVectorX.x + (_vectorX_Steps / 2) ; + CGFloat xPos = temp + (((value - _AxisX_minValue)/_AxisX_step) * _vectorX_Steps) ; + return xPos; + } + else { + float temp = _startPointVectorY.y - (_vectorY_Steps / 2) ; + CGFloat yPos = temp - (((value - _AxisY_minValue) /_AxisY_step) * _vectorY_Steps); + return yPos; + } + return 0; +} + +#pragma mark - Update Chart Data + +- (void)updateChartData:(NSArray *)data +{ + _chartData = data; + + // will be work in future. +} + +#pragma drawing methods + +- (void)drawRect:(CGRect)rect +{ + [super drawRect:rect]; + + CGContextRef context = UIGraphicsGetCurrentContext(); + if (_showCoordinateAxis) { + CGContextSetStrokeColorWithColor(context, [_axisColor CGColor]); + CGContextSetLineWidth(context, _axisWidth); + //drawing x vector + CGContextMoveToPoint(context, _startPoint.x, _startPoint.y); + CGContextAddLineToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); + //drawing y vector + CGContextMoveToPoint(context, _startPoint.x, _startPoint.y); + CGContextAddLineToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); + //drawing x arrow vector + CGContextMoveToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); + CGContextAddLineToPoint(context, _endPointVecotrX.x - 5, _endPointVecotrX.y + 3); + CGContextMoveToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); + CGContextAddLineToPoint(context, _endPointVecotrX.x - 5, _endPointVecotrX.y - 3); + //drawing y arrow vector + CGContextMoveToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); + CGContextAddLineToPoint(context, _endPointVecotrY.x - 3, _endPointVecotrY.y + 5); + CGContextMoveToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); + CGContextAddLineToPoint(context, _endPointVecotrY.x + 3, _endPointVecotrY.y + 5); + } + + if (_showLabel) { + //drawing x steps vector and putting axis x labels + float temp = _startPointVectorX.x + (_vectorX_Steps / 2) ; + for (int i = 0; i < _axisX_labels.count; i++) { + UIBezierPath *path = [UIBezierPath bezierPath]; + [path moveToPoint:CGPointMake(temp, _startPointVectorX.y - 2)]; + [path addLineToPoint:CGPointMake(temp, _startPointVectorX.y + 3)]; + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; + shapeLayer.path = [path CGPath]; + shapeLayer.strokeColor = [_axisColor CGColor]; + shapeLayer.lineWidth = _axisWidth; + shapeLayer.fillColor = [_axisColor CGColor]; + [self.horizentalLinepathLayer addObject:shapeLayer]; + [self.layer addSublayer:shapeLayer]; + UILabel *lb = [_axisX_labels objectAtIndex:i] ; + [self showXLabel:lb InPosition:CGPointMake(temp - 15, _startPointVectorX.y + 10 )]; + temp = temp + _vectorX_Steps ; + } + //drawing y steps vector and putting axis x labels + temp = _startPointVectorY.y - (_vectorY_Steps / 2) ; + for (int i = 0; i < _axisY_labels.count; i++) { + UIBezierPath *path = [UIBezierPath bezierPath]; + [path moveToPoint:CGPointMake(_startPointVectorY.x - 3, temp)]; + [path addLineToPoint:CGPointMake( _startPointVectorY.x + 2, temp)]; + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; + shapeLayer.path = [path CGPath]; + shapeLayer.strokeColor = [_axisColor CGColor]; + shapeLayer.lineWidth = _axisWidth; + shapeLayer.fillColor = [_axisColor CGColor]; + [self.verticalLineLayer addObject:shapeLayer]; + [self.layer addSublayer:shapeLayer]; + UILabel *lb = [_axisY_labels objectAtIndex:i]; + [self showXLabel:lb InPosition:CGPointMake(_startPointVectorY.x - 30, temp - 5)]; + temp = temp - _vectorY_Steps ; + } + } + CGContextDrawPath(context, kCGPathStroke); +} + +- (CAShapeLayer*) drawingPointsForChartData : (PNScatterChartData *) chartData AndWithX : (CGFloat) X AndWithY : (CGFloat) Y +{ + if (chartData.inflexionPointStyle == PNScatterChartPointStyleCircle) { + float radius = chartData.size; + CAShapeLayer *circle = [CAShapeLayer layer]; + // Make a circular shape + circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(X - radius, Y - radius, 2.0*radius, 2.0*radius) + cornerRadius:radius].CGPath; + // Configure the appearence of the circle + circle.fillColor = [chartData.fillColor CGColor]; + circle.strokeColor = [chartData.strokeColor CGColor]; + circle.lineWidth = 1; + + // Add to parent layer + return circle; + } + else if (chartData.inflexionPointStyle == PNScatterChartPointStyleSquare) { + float side = chartData.size; + CAShapeLayer *square = [CAShapeLayer layer]; + // Make a circular shape + square.path = [UIBezierPath bezierPathWithRect:CGRectMake(X - (side/2) , Y - (side/2), side, side)].CGPath ; + // Configure the apperence of the circle + square.fillColor = [chartData.fillColor CGColor]; + square.strokeColor = [chartData.strokeColor CGColor]; + square.lineWidth = 1; + + // Add to parent layer + return square; + } + else { + // you cann add your own scatter chart point here + } + return nil ; +} + +- (void) drawLineFromPoint : (CGPoint) startPoint ToPoint : (CGPoint) endPoint WithLineWith : (CGFloat) lineWidth AndWithColor : (UIColor*) color{ + + // call the same method on a background thread + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + if (self.displayAnimated) { + [NSThread sleepForTimeInterval:2]; + } + // calculating start and end point + __block CGFloat startX = [self mappingIsForAxisX:true WithValue:startPoint.x]; + __block CGFloat startY = [self mappingIsForAxisX:false WithValue:startPoint.y]; + __block CGFloat endX = [self mappingIsForAxisX:true WithValue:endPoint.x]; + __block CGFloat endY = [self mappingIsForAxisX:false WithValue:endPoint.y]; + // update UI on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + // drawing path between two points + UIBezierPath *path = [UIBezierPath bezierPath]; + [path moveToPoint:CGPointMake(startX, startY)]; + [path addLineToPoint:CGPointMake(endX, endY)]; + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; + shapeLayer.path = [path CGPath]; + shapeLayer.strokeColor = [color CGColor]; + shapeLayer.lineWidth = lineWidth; + shapeLayer.fillColor = [color CGColor]; + // adding animation to path + [self addStrokeEndAnimationIfNeededToLayer:shapeLayer]; + [self.layer addSublayer:shapeLayer]; + }); + }); +} + +- (void)addStrokeEndAnimationIfNeededToLayer:(CAShapeLayer *)shapeLayer{ + + if (self.displayAnimated) { + CABasicAnimation *animateStrokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + animateStrokeEnd.duration = _duration; + animateStrokeEnd.fromValue = [NSNumber numberWithFloat:0.0f]; + animateStrokeEnd.toValue = [NSNumber numberWithFloat:1.0f]; + [shapeLayer addAnimation:animateStrokeEnd forKey:nil]; + } +} + +@end diff --git a/PNChartdemo/PNChart/PNScatterChartData.h b/PNChartdemo/PNChart/PNScatterChartData.h new file mode 100755 index 0000000..40b7b0e --- /dev/null +++ b/PNChartdemo/PNChart/PNScatterChartData.h @@ -0,0 +1,38 @@ +// +// PNScatterChartData.h +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +typedef NS_ENUM(NSUInteger, PNScatterChartPointStyle) { + PNScatterChartPointStyleCircle = 0, + PNScatterChartPointStyleSquare = 1, +}; + +@class PNScatterChartDataItem; + +typedef PNScatterChartDataItem *(^LCScatterChartDataGetter)(NSUInteger item); + +@interface PNScatterChartData : NSObject + +@property (strong) UIColor *fillColor; +@property (strong) UIColor *strokeColor; + +@property NSUInteger itemCount; +@property (copy) LCScatterChartDataGetter getData; + +@property (nonatomic, assign) PNScatterChartPointStyle inflexionPointStyle; + +/** + * If PNLineChartPointStyle is circle, this returns the circle's diameter. + * If PNLineChartPointStyle is square, each point is a square with each side equal in length to this value. + */ +@property (nonatomic, assign) CGFloat size; + + +@end diff --git a/PNChartdemo/PNChart/PNScatterChartData.m b/PNChartdemo/PNChart/PNScatterChartData.m new file mode 100755 index 0000000..a53a5c2 --- /dev/null +++ b/PNChartdemo/PNChart/PNScatterChartData.m @@ -0,0 +1,31 @@ +// +// PNScatterChartData.m +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import "PNScatterChartData.h" + +@implementation PNScatterChartData + +- (id)init +{ + self = [super init]; + if (self) { + [self setupDefaultValues]; + } + + return self; +} + +- (void)setupDefaultValues +{ + _inflexionPointStyle = PNScatterChartPointStyleCircle; + _fillColor = [UIColor grayColor]; + _strokeColor = [UIColor clearColor]; + _size = 3 ; +} + +@end diff --git a/PNChartdemo/PNChart/PNScatterChartDataItem.h b/PNChartdemo/PNChart/PNScatterChartDataItem.h new file mode 100755 index 0000000..f38ee4e --- /dev/null +++ b/PNChartdemo/PNChart/PNScatterChartDataItem.h @@ -0,0 +1,19 @@ +// +// PNScatterChartDataItem.h +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +@interface PNScatterChartDataItem : NSObject + ++ (PNScatterChartDataItem *)dataItemWithX:(CGFloat)x AndWithY:(CGFloat)y; + +@property (readonly) CGFloat x; // should be within the x range +@property (readonly) CGFloat y; // should be within the y range + +@end diff --git a/PNChartdemo/PNChart/PNScatterChartDataItem.m b/PNChartdemo/PNChart/PNScatterChartDataItem.m new file mode 100755 index 0000000..a454459 --- /dev/null +++ b/PNChartdemo/PNChart/PNScatterChartDataItem.m @@ -0,0 +1,37 @@ +// +// PNScatterChartDataItem.m +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import "PNScatterChartDataItem.h" + +@interface PNScatterChartDataItem () + +- (id)initWithX:(CGFloat)x AndWithY:(CGFloat)y; + +@property (readwrite) CGFloat x; // should be within the x range +@property (readwrite) CGFloat y; // should be within the y range + +@end + +@implementation PNScatterChartDataItem + ++ (PNScatterChartDataItem *)dataItemWithX:(CGFloat)x AndWithY:(CGFloat)y +{ + return [[PNScatterChartDataItem alloc] initWithX:x AndWithY:y]; +} + +- (id)initWithX:(CGFloat)x AndWithY:(CGFloat)y +{ + if ((self = [super init])) { + self.x = x; + self.y = y; + } + + return self; +} + +@end diff --git a/PNChartdemo/PNChartdemo.xcodeproj/project.pbxproj b/PNChartdemo/PNChartdemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..651d89c --- /dev/null +++ b/PNChartdemo/PNChartdemo.xcodeproj/project.pbxproj @@ -0,0 +1,479 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + E56E9F2E1E68F54300412830 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F2D1E68F54300412830 /* main.m */; }; + E56E9F311E68F54300412830 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F301E68F54300412830 /* AppDelegate.m */; }; + E56E9F341E68F54300412830 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F331E68F54300412830 /* ViewController.m */; }; + E56E9F371E68F54300412830 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E56E9F351E68F54300412830 /* Main.storyboard */; }; + E56E9F391E68F54300412830 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E56E9F381E68F54300412830 /* Assets.xcassets */; }; + E56E9F3C1E68F54300412830 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E56E9F3A1E68F54300412830 /* LaunchScreen.storyboard */; }; + E56E9F661E68F55F00412830 /* PNBar.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F451E68F55F00412830 /* PNBar.m */; }; + E56E9F671E68F55F00412830 /* PNBarChart.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F471E68F55F00412830 /* PNBarChart.m */; }; + E56E9F681E68F55F00412830 /* PNChartLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F4B1E68F55F00412830 /* PNChartLabel.m */; }; + E56E9F691E68F55F00412830 /* PNCircleChart.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F4D1E68F55F00412830 /* PNCircleChart.m */; }; + E56E9F6A1E68F55F00412830 /* PNColor.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F4F1E68F55F00412830 /* PNColor.m */; }; + E56E9F6B1E68F55F00412830 /* PNGenericChart.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F511E68F55F00412830 /* PNGenericChart.m */; }; + E56E9F6C1E68F55F00412830 /* PNLineChart.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F531E68F55F00412830 /* PNLineChart.m */; }; + E56E9F6D1E68F55F00412830 /* PNLineChartData.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F551E68F55F00412830 /* PNLineChartData.m */; }; + E56E9F6E1E68F55F00412830 /* PNLineChartDataItem.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F571E68F55F00412830 /* PNLineChartDataItem.m */; }; + E56E9F6F1E68F55F00412830 /* PNPieChart.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F591E68F55F00412830 /* PNPieChart.m */; }; + E56E9F701E68F55F00412830 /* PNPieChartDataItem.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F5B1E68F55F00412830 /* PNPieChartDataItem.m */; }; + E56E9F711E68F55F00412830 /* PNRadarChart.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F5D1E68F55F00412830 /* PNRadarChart.m */; }; + E56E9F721E68F55F00412830 /* PNRadarChartDataItem.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F5F1E68F55F00412830 /* PNRadarChartDataItem.m */; }; + E56E9F731E68F55F00412830 /* PNScatterChart.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F611E68F55F00412830 /* PNScatterChart.m */; }; + E56E9F741E68F55F00412830 /* PNScatterChartData.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F631E68F55F00412830 /* PNScatterChartData.m */; }; + E56E9F751E68F55F00412830 /* PNScatterChartDataItem.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F651E68F55F00412830 /* PNScatterChartDataItem.m */; }; + E56E9F781E68F59F00412830 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56E9F771E68F59F00412830 /* CoreGraphics.framework */; }; + E56E9F7A1E68F5A900412830 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56E9F791E68F5A900412830 /* QuartzCore.framework */; }; + E56E9F7C1E68F5B300412830 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56E9F7B1E68F5B300412830 /* UIKit.framework */; }; + E56E9F7E1E68F5B800412830 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E56E9F7D1E68F5B800412830 /* Foundation.framework */; }; + E56E9F861E68F84700412830 /* UICountingLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F851E68F84700412830 /* UICountingLabel.m */; }; + E56E9F891E68F89200412830 /* PCChartsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F881E68F89200412830 /* PCChartsTableViewController.m */; }; + E56E9F8C1E68F8F600412830 /* PCChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E56E9F8B1E68F8F600412830 /* PCChartViewController.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + E56E9F291E68F54300412830 /* PNChartdemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PNChartdemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E56E9F2D1E68F54300412830 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; + E56E9F2F1E68F54300412830 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; + E56E9F301E68F54300412830 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; + E56E9F321E68F54300412830 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; }; + E56E9F331E68F54300412830 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; }; + E56E9F361E68F54300412830 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; + E56E9F381E68F54300412830 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; + E56E9F3B1E68F54300412830 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; + E56E9F3D1E68F54300412830 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + E56E9F441E68F55F00412830 /* PNBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNBar.h; sourceTree = "<group>"; }; + E56E9F451E68F55F00412830 /* PNBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNBar.m; sourceTree = "<group>"; }; + E56E9F461E68F55F00412830 /* PNBarChart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNBarChart.h; sourceTree = "<group>"; }; + E56E9F471E68F55F00412830 /* PNBarChart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNBarChart.m; sourceTree = "<group>"; }; + E56E9F481E68F55F00412830 /* PNChart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNChart.h; sourceTree = "<group>"; }; + E56E9F491E68F55F00412830 /* PNChartDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNChartDelegate.h; sourceTree = "<group>"; }; + E56E9F4A1E68F55F00412830 /* PNChartLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNChartLabel.h; sourceTree = "<group>"; }; + E56E9F4B1E68F55F00412830 /* PNChartLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNChartLabel.m; sourceTree = "<group>"; }; + E56E9F4C1E68F55F00412830 /* PNCircleChart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNCircleChart.h; sourceTree = "<group>"; }; + E56E9F4D1E68F55F00412830 /* PNCircleChart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNCircleChart.m; sourceTree = "<group>"; }; + E56E9F4E1E68F55F00412830 /* PNColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNColor.h; sourceTree = "<group>"; }; + E56E9F4F1E68F55F00412830 /* PNColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNColor.m; sourceTree = "<group>"; }; + E56E9F501E68F55F00412830 /* PNGenericChart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNGenericChart.h; sourceTree = "<group>"; }; + E56E9F511E68F55F00412830 /* PNGenericChart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNGenericChart.m; sourceTree = "<group>"; }; + E56E9F521E68F55F00412830 /* PNLineChart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNLineChart.h; sourceTree = "<group>"; }; + E56E9F531E68F55F00412830 /* PNLineChart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNLineChart.m; sourceTree = "<group>"; }; + E56E9F541E68F55F00412830 /* PNLineChartData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNLineChartData.h; sourceTree = "<group>"; }; + E56E9F551E68F55F00412830 /* PNLineChartData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNLineChartData.m; sourceTree = "<group>"; }; + E56E9F561E68F55F00412830 /* PNLineChartDataItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNLineChartDataItem.h; sourceTree = "<group>"; }; + E56E9F571E68F55F00412830 /* PNLineChartDataItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNLineChartDataItem.m; sourceTree = "<group>"; }; + E56E9F581E68F55F00412830 /* PNPieChart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNPieChart.h; sourceTree = "<group>"; }; + E56E9F591E68F55F00412830 /* PNPieChart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNPieChart.m; sourceTree = "<group>"; }; + E56E9F5A1E68F55F00412830 /* PNPieChartDataItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNPieChartDataItem.h; sourceTree = "<group>"; }; + E56E9F5B1E68F55F00412830 /* PNPieChartDataItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNPieChartDataItem.m; sourceTree = "<group>"; }; + E56E9F5C1E68F55F00412830 /* PNRadarChart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNRadarChart.h; sourceTree = "<group>"; }; + E56E9F5D1E68F55F00412830 /* PNRadarChart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNRadarChart.m; sourceTree = "<group>"; }; + E56E9F5E1E68F55F00412830 /* PNRadarChartDataItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNRadarChartDataItem.h; sourceTree = "<group>"; }; + E56E9F5F1E68F55F00412830 /* PNRadarChartDataItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNRadarChartDataItem.m; sourceTree = "<group>"; }; + E56E9F601E68F55F00412830 /* PNScatterChart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNScatterChart.h; sourceTree = "<group>"; }; + E56E9F611E68F55F00412830 /* PNScatterChart.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNScatterChart.m; sourceTree = "<group>"; }; + E56E9F621E68F55F00412830 /* PNScatterChartData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNScatterChartData.h; sourceTree = "<group>"; }; + E56E9F631E68F55F00412830 /* PNScatterChartData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNScatterChartData.m; sourceTree = "<group>"; }; + E56E9F641E68F55F00412830 /* PNScatterChartDataItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PNScatterChartDataItem.h; sourceTree = "<group>"; }; + E56E9F651E68F55F00412830 /* PNScatterChartDataItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PNScatterChartDataItem.m; sourceTree = "<group>"; }; + E56E9F771E68F59F00412830 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + E56E9F791E68F5A900412830 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + E56E9F7B1E68F5B300412830 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + E56E9F7D1E68F5B800412830 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + E56E9F7F1E68F64800412830 /* PNChartDemo-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PNChartDemo-Prefix.pch"; sourceTree = "<group>"; }; + E56E9F841E68F84700412830 /* UICountingLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UICountingLabel.h; sourceTree = "<group>"; }; + E56E9F851E68F84700412830 /* UICountingLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UICountingLabel.m; sourceTree = "<group>"; }; + E56E9F871E68F89200412830 /* PCChartsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCChartsTableViewController.h; sourceTree = "<group>"; }; + E56E9F881E68F89200412830 /* PCChartsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PCChartsTableViewController.m; sourceTree = "<group>"; }; + E56E9F8A1E68F8F600412830 /* PCChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCChartViewController.h; sourceTree = "<group>"; }; + E56E9F8B1E68F8F600412830 /* PCChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PCChartViewController.m; sourceTree = "<group>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E56E9F261E68F54300412830 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E56E9F7E1E68F5B800412830 /* Foundation.framework in Frameworks */, + E56E9F7C1E68F5B300412830 /* UIKit.framework in Frameworks */, + E56E9F7A1E68F5A900412830 /* QuartzCore.framework in Frameworks */, + E56E9F781E68F59F00412830 /* CoreGraphics.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E56E9F201E68F54300412830 = { + isa = PBXGroup; + children = ( + E56E9F431E68F55F00412830 /* PNChart */, + E56E9F2B1E68F54300412830 /* PNChartdemo */, + E56E9F2A1E68F54300412830 /* Products */, + E56E9F761E68F59E00412830 /* Frameworks */, + ); + sourceTree = "<group>"; + }; + E56E9F2A1E68F54300412830 /* Products */ = { + isa = PBXGroup; + children = ( + E56E9F291E68F54300412830 /* PNChartdemo.app */, + ); + name = Products; + sourceTree = "<group>"; + }; + E56E9F2B1E68F54300412830 /* PNChartdemo */ = { + isa = PBXGroup; + children = ( + E56E9F8A1E68F8F600412830 /* PCChartViewController.h */, + E56E9F8B1E68F8F600412830 /* PCChartViewController.m */, + E56E9F871E68F89200412830 /* PCChartsTableViewController.h */, + E56E9F881E68F89200412830 /* PCChartsTableViewController.m */, + E56E9F831E68F84700412830 /* UICountingLabel */, + E56E9F2F1E68F54300412830 /* AppDelegate.h */, + E56E9F301E68F54300412830 /* AppDelegate.m */, + E56E9F321E68F54300412830 /* ViewController.h */, + E56E9F331E68F54300412830 /* ViewController.m */, + E56E9F351E68F54300412830 /* Main.storyboard */, + E56E9F381E68F54300412830 /* Assets.xcassets */, + E56E9F3A1E68F54300412830 /* LaunchScreen.storyboard */, + E56E9F2C1E68F54300412830 /* Supporting Files */, + ); + path = PNChartdemo; + sourceTree = "<group>"; + }; + E56E9F2C1E68F54300412830 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + E56E9F7F1E68F64800412830 /* PNChartDemo-Prefix.pch */, + E56E9F3D1E68F54300412830 /* Info.plist */, + E56E9F2D1E68F54300412830 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = "<group>"; + }; + E56E9F431E68F55F00412830 /* PNChart */ = { + isa = PBXGroup; + children = ( + E56E9F441E68F55F00412830 /* PNBar.h */, + E56E9F451E68F55F00412830 /* PNBar.m */, + E56E9F461E68F55F00412830 /* PNBarChart.h */, + E56E9F471E68F55F00412830 /* PNBarChart.m */, + E56E9F481E68F55F00412830 /* PNChart.h */, + E56E9F491E68F55F00412830 /* PNChartDelegate.h */, + E56E9F4A1E68F55F00412830 /* PNChartLabel.h */, + E56E9F4B1E68F55F00412830 /* PNChartLabel.m */, + E56E9F4C1E68F55F00412830 /* PNCircleChart.h */, + E56E9F4D1E68F55F00412830 /* PNCircleChart.m */, + E56E9F4E1E68F55F00412830 /* PNColor.h */, + E56E9F4F1E68F55F00412830 /* PNColor.m */, + E56E9F501E68F55F00412830 /* PNGenericChart.h */, + E56E9F511E68F55F00412830 /* PNGenericChart.m */, + E56E9F521E68F55F00412830 /* PNLineChart.h */, + E56E9F531E68F55F00412830 /* PNLineChart.m */, + E56E9F541E68F55F00412830 /* PNLineChartData.h */, + E56E9F551E68F55F00412830 /* PNLineChartData.m */, + E56E9F561E68F55F00412830 /* PNLineChartDataItem.h */, + E56E9F571E68F55F00412830 /* PNLineChartDataItem.m */, + E56E9F581E68F55F00412830 /* PNPieChart.h */, + E56E9F591E68F55F00412830 /* PNPieChart.m */, + E56E9F5A1E68F55F00412830 /* PNPieChartDataItem.h */, + E56E9F5B1E68F55F00412830 /* PNPieChartDataItem.m */, + E56E9F5C1E68F55F00412830 /* PNRadarChart.h */, + E56E9F5D1E68F55F00412830 /* PNRadarChart.m */, + E56E9F5E1E68F55F00412830 /* PNRadarChartDataItem.h */, + E56E9F5F1E68F55F00412830 /* PNRadarChartDataItem.m */, + E56E9F601E68F55F00412830 /* PNScatterChart.h */, + E56E9F611E68F55F00412830 /* PNScatterChart.m */, + E56E9F621E68F55F00412830 /* PNScatterChartData.h */, + E56E9F631E68F55F00412830 /* PNScatterChartData.m */, + E56E9F641E68F55F00412830 /* PNScatterChartDataItem.h */, + E56E9F651E68F55F00412830 /* PNScatterChartDataItem.m */, + ); + path = PNChart; + sourceTree = "<group>"; + }; + E56E9F761E68F59E00412830 /* Frameworks */ = { + isa = PBXGroup; + children = ( + E56E9F7D1E68F5B800412830 /* Foundation.framework */, + E56E9F7B1E68F5B300412830 /* UIKit.framework */, + E56E9F791E68F5A900412830 /* QuartzCore.framework */, + E56E9F771E68F59F00412830 /* CoreGraphics.framework */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + E56E9F831E68F84700412830 /* UICountingLabel */ = { + isa = PBXGroup; + children = ( + E56E9F841E68F84700412830 /* UICountingLabel.h */, + E56E9F851E68F84700412830 /* UICountingLabel.m */, + ); + path = UICountingLabel; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E56E9F281E68F54300412830 /* PNChartdemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = E56E9F401E68F54300412830 /* Build configuration list for PBXNativeTarget "PNChartdemo" */; + buildPhases = ( + E56E9F251E68F54300412830 /* Sources */, + E56E9F261E68F54300412830 /* Frameworks */, + E56E9F271E68F54300412830 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PNChartdemo; + productName = PNChartdemo; + productReference = E56E9F291E68F54300412830 /* PNChartdemo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E56E9F211E68F54300412830 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0820; + ORGANIZATIONNAME = WindShan; + TargetAttributes = { + E56E9F281E68F54300412830 = { + CreatedOnToolsVersion = 8.2.1; + DevelopmentTeam = VGXA77XL6T; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = E56E9F241E68F54300412830 /* Build configuration list for PBXProject "PNChartdemo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E56E9F201E68F54300412830; + productRefGroup = E56E9F2A1E68F54300412830 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E56E9F281E68F54300412830 /* PNChartdemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E56E9F271E68F54300412830 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E56E9F3C1E68F54300412830 /* LaunchScreen.storyboard in Resources */, + E56E9F391E68F54300412830 /* Assets.xcassets in Resources */, + E56E9F371E68F54300412830 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E56E9F251E68F54300412830 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E56E9F6C1E68F55F00412830 /* PNLineChart.m in Sources */, + E56E9F671E68F55F00412830 /* PNBarChart.m in Sources */, + E56E9F6A1E68F55F00412830 /* PNColor.m in Sources */, + E56E9F891E68F89200412830 /* PCChartsTableViewController.m in Sources */, + E56E9F711E68F55F00412830 /* PNRadarChart.m in Sources */, + E56E9F721E68F55F00412830 /* PNRadarChartDataItem.m in Sources */, + E56E9F341E68F54300412830 /* ViewController.m in Sources */, + E56E9F661E68F55F00412830 /* PNBar.m in Sources */, + E56E9F741E68F55F00412830 /* PNScatterChartData.m in Sources */, + E56E9F6E1E68F55F00412830 /* PNLineChartDataItem.m in Sources */, + E56E9F681E68F55F00412830 /* PNChartLabel.m in Sources */, + E56E9F311E68F54300412830 /* AppDelegate.m in Sources */, + E56E9F751E68F55F00412830 /* PNScatterChartDataItem.m in Sources */, + E56E9F701E68F55F00412830 /* PNPieChartDataItem.m in Sources */, + E56E9F2E1E68F54300412830 /* main.m in Sources */, + E56E9F6D1E68F55F00412830 /* PNLineChartData.m in Sources */, + E56E9F731E68F55F00412830 /* PNScatterChart.m in Sources */, + E56E9F691E68F55F00412830 /* PNCircleChart.m in Sources */, + E56E9F8C1E68F8F600412830 /* PCChartViewController.m in Sources */, + E56E9F861E68F84700412830 /* UICountingLabel.m in Sources */, + E56E9F6F1E68F55F00412830 /* PNPieChart.m in Sources */, + E56E9F6B1E68F55F00412830 /* PNGenericChart.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + E56E9F351E68F54300412830 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E56E9F361E68F54300412830 /* Base */, + ); + name = Main.storyboard; + sourceTree = "<group>"; + }; + E56E9F3A1E68F54300412830 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E56E9F3B1E68F54300412830 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = "<group>"; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + E56E9F3E1E68F54300412830 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + E56E9F3F1E68F54300412830 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + E56E9F411E68F54300412830 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = VGXA77XL6T; + GCC_PREFIX_HEADER = "PNChartdemo/PNChartDemo-Prefix.pch"; + INFOPLIST_FILE = PNChartdemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.moral.PNChartdemo.PNChartdemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + E56E9F421E68F54300412830 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = VGXA77XL6T; + GCC_PREFIX_HEADER = "PNChartdemo/PNChartDemo-Prefix.pch"; + INFOPLIST_FILE = PNChartdemo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.moral.PNChartdemo.PNChartdemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E56E9F241E68F54300412830 /* Build configuration list for PBXProject "PNChartdemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E56E9F3E1E68F54300412830 /* Debug */, + E56E9F3F1E68F54300412830 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E56E9F401E68F54300412830 /* Build configuration list for PBXNativeTarget "PNChartdemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E56E9F411E68F54300412830 /* Debug */, + E56E9F421E68F54300412830 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = E56E9F211E68F54300412830 /* Project object */; +} diff --git a/camerademo/camerademo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/PNChartdemo/PNChartdemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 69% rename from camerademo/camerademo.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to PNChartdemo/PNChartdemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata index ac29a49..3674b57 100644 --- a/camerademo/camerademo.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/PNChartdemo/PNChartdemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ <Workspace version = "1.0"> <FileRef - location = "self:camerademo.xcodeproj"> + location = "self:PNChartdemo.xcodeproj"> </FileRef> </Workspace> diff --git a/PNChartdemo/PNChartdemo.xcodeproj/project.xcworkspace/xcuserdata/WindShan.xcuserdatad/UserInterfaceState.xcuserstate b/PNChartdemo/PNChartdemo.xcodeproj/project.xcworkspace/xcuserdata/WindShan.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..ee124fb --- /dev/null +++ b/PNChartdemo/PNChartdemo.xcodeproj/project.xcworkspace/xcuserdata/WindShan.xcuserdatad/UserInterfaceState.xcuserstate Binary files differ diff --git a/PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..ce2e85d --- /dev/null +++ b/PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Bucket + type = "1" + version = "2.0"> + <Breakpoints> + <BreakpointProxy + BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> + <BreakpointContent + shouldBeEnabled = "Yes" + ignoreCount = "0" + continueAfterRunningActions = "No" + filePath = "PNChartdemo/PCChartViewController.m" + timestampString = "510197037.819375" + startingColumnNumber = "9223372036854775807" + endingColumnNumber = "9223372036854775807" + startingLineNumber = "403" + endingLineNumber = "403" + landmarkName = "-rightSwitchChanged:" + landmarkType = "7"> + </BreakpointContent> + </BreakpointProxy> + <BreakpointProxy + BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> + <BreakpointContent + shouldBeEnabled = "Yes" + ignoreCount = "0" + continueAfterRunningActions = "No" + filePath = "PNChartdemo/PCChartViewController.m" + timestampString = "510197037.819375" + startingColumnNumber = "9223372036854775807" + endingColumnNumber = "9223372036854775807" + startingLineNumber = "412" + endingLineNumber = "412" + landmarkName = "-centerSwitchChanged:" + landmarkType = "7"> + </BreakpointContent> + </BreakpointProxy> + <BreakpointProxy + BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> + <BreakpointContent + shouldBeEnabled = "Yes" + ignoreCount = "0" + continueAfterRunningActions = "No" + filePath = "PNChartdemo/PCChartViewController.m" + timestampString = "510197037.819375" + startingColumnNumber = "9223372036854775807" + endingColumnNumber = "9223372036854775807" + startingLineNumber = "433" + endingLineNumber = "433" + landmarkName = "-leftSwitchChanged:" + landmarkType = "7"> + </BreakpointContent> + </BreakpointProxy> + </Breakpoints> +</Bucket> diff --git a/PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/PNChartdemo.xcscheme b/PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/PNChartdemo.xcscheme new file mode 100644 index 0000000..8666a26 --- /dev/null +++ b/PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/PNChartdemo.xcscheme @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0820" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "E56E9F281E68F54300412830" + BuildableName = "PNChartdemo.app" + BlueprintName = "PNChartdemo" + ReferencedContainer = "container:PNChartdemo.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES"> + <Testables> + </Testables> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "E56E9F281E68F54300412830" + BuildableName = "PNChartdemo.app" + BlueprintName = "PNChartdemo" + ReferencedContainer = "container:PNChartdemo.xcodeproj"> + </BuildableReference> + </MacroExpansion> + <AdditionalOptions> + </AdditionalOptions> + </TestAction> + <LaunchAction + buildConfiguration = "Debug" + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + debugServiceExtension = "internal" + allowLocationSimulation = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "E56E9F281E68F54300412830" + BuildableName = "PNChartdemo.app" + BlueprintName = "PNChartdemo" + ReferencedContainer = "container:PNChartdemo.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + buildConfiguration = "Release" + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable + runnableDebuggingMode = "0"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "E56E9F281E68F54300412830" + BuildableName = "PNChartdemo.app" + BlueprintName = "PNChartdemo" + ReferencedContainer = "container:PNChartdemo.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/camerademo/camerademo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/xcschememanagement.plist b/PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 84% rename from camerademo/camerademo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/xcschememanagement.plist rename to PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/xcschememanagement.plist index 01e9d0f..d85d839 100644 --- a/camerademo/camerademo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/PNChartdemo/PNChartdemo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,7 +4,7 @@ <dict> <key>SchemeUserState</key> <dict> - <key>camerademo.xcscheme</key> + <key>PNChartdemo.xcscheme</key> <dict> <key>orderHint</key> <integer>0</integer> @@ -12,7 +12,7 @@ </dict> <key>SuppressBuildableAutocreation</key> <dict> - <key>E52F59EA1E5C21890084B3E2</key> + <key>E56E9F281E68F54300412830</key> <dict> <key>primary</key> <true/> diff --git a/camerademo/camerademo/AppDelegate.h b/PNChartdemo/PNChartdemo/AppDelegate.h similarity index 80% rename from camerademo/camerademo/AppDelegate.h rename to PNChartdemo/PNChartdemo/AppDelegate.h index ca2a55e..e933d75 100644 --- a/camerademo/camerademo/AppDelegate.h +++ b/PNChartdemo/PNChartdemo/AppDelegate.h @@ -1,8 +1,8 @@ // // AppDelegate.h -// camerademo +// PNChartdemo // -// Created by WindShan on 2017/2/21. +// Created by WindShan on 2017/3/3. // Copyright �� 2017��� WindShan. All rights reserved. // diff --git a/camerademo/camerademo/AppDelegate.m b/PNChartdemo/PNChartdemo/AppDelegate.m similarity index 96% rename from camerademo/camerademo/AppDelegate.m rename to PNChartdemo/PNChartdemo/AppDelegate.m index c5b854e..93c0cbb 100644 --- a/camerademo/camerademo/AppDelegate.m +++ b/PNChartdemo/PNChartdemo/AppDelegate.m @@ -1,8 +1,8 @@ // // AppDelegate.m -// camerademo +// PNChartdemo // -// Created by WindShan on 2017/2/21. +// Created by WindShan on 2017/3/3. // Copyright �� 2017��� WindShan. All rights reserved. // diff --git a/camerademo/camerademo/Assets.xcassets/AppIcon.appiconset/Contents.json b/PNChartdemo/PNChartdemo/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from camerademo/camerademo/Assets.xcassets/AppIcon.appiconset/Contents.json rename to PNChartdemo/PNChartdemo/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/camerademo/camerademo/Base.lproj/LaunchScreen.storyboard b/PNChartdemo/PNChartdemo/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from camerademo/camerademo/Base.lproj/LaunchScreen.storyboard rename to PNChartdemo/PNChartdemo/Base.lproj/LaunchScreen.storyboard diff --git a/PNChartdemo/PNChartdemo/Base.lproj/Main.storyboard b/PNChartdemo/PNChartdemo/Base.lproj/Main.storyboard new file mode 100755 index 0000000..f75524f --- /dev/null +++ b/PNChartdemo/PNChartdemo/Base.lproj/Main.storyboard @@ -0,0 +1,300 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="9Rt-UT-IxH"> + <device id="retina4_7" orientation="portrait"> + <adaptation id="fullscreen"/> + </device> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/> + <capability name="Constraints to layout margins" minToolsVersion="6.0"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <scenes> + <!--PNChart--> + <scene sceneID="lC9-iu-Smd"> + <objects> + <viewController id="Tha-Wr-sPW" customClass="PCChartViewController" sceneMemberID="viewController"> + <layoutGuides> + <viewControllerLayoutGuide type="top" id="znr-YO-4a4"/> + <viewControllerLayoutGuide type="bottom" id="61B-jM-QAH"/> + </layoutGuides> + <view key="view" contentMode="scaleToFill" id="PPb-b8-nBo"> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="L3F-13-Wf5"> + <rect key="frame" x="140" y="364" width="95" height="30"/> + <state key="normal" title="Change Value"> + <color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + </state> + <connections> + <action selector="changeValue:" destination="Tha-Wr-sPW" eventType="touchUpInside" id="zeG-Cp-Wjs"/> + </connections> + </button> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FUU-vZ-jMd"> + <rect key="frame" x="159" y="81" width="57" height="32"/> + <fontDescription key="fontDescription" name="Avenir-Medium" family="Avenir" pointSize="23"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Percentage" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="IKu-qh-ksi"> + <rect key="frame" x="16" y="539" width="88" height="21"/> + <fontDescription key="fontDescription" type="system" pointSize="17"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Show Labels" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ifm-a9-Wkq"> + <rect key="frame" x="260" y="538" width="99" height="21"/> + <constraints> + <constraint firstAttribute="width" constant="99" id="KZW-Ru-GZO"/> + </constraints> + <fontDescription key="fontDescription" type="system" pointSize="17"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="kav-3r-blI"> + <rect key="frame" x="16" y="434" width="51" height="31"/> + <connections> + <action selector="centerSwitchChanged:" destination="Tha-Wr-sPW" eventType="valueChanged" id="ETI-hb-d8F"/> + </connections> + </switch> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Multiple Selection" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sMF-o9-dZX"> + <rect key="frame" x="16" y="471" width="137" height="21"/> + <fontDescription key="fontDescription" type="system" pointSize="17"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ORA-mb-hJl"> + <rect key="frame" x="16" y="500" width="51" height="31"/> + <connections> + <action selector="leftSwitchChanged:" destination="Tha-Wr-sPW" eventType="valueChanged" id="q9T-QK-Sas"/> + </connections> + </switch> + <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Mnm-Is-oih" userLabel="Animations Switch"> + <rect key="frame" x="310" y="434" width="51" height="31"/> + <connections> + <action selector="animationsSwitchChanged:" destination="Tha-Wr-sPW" eventType="valueChanged" id="lC4-5M-rlC"/> + </connections> + </switch> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Animations" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lc1-oP-FNw"> + <rect key="frame" x="273" y="471" width="86" height="21"/> + <fontDescription key="fontDescription" type="system" pointSize="17"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qR7-40-7ir"> + <rect key="frame" x="310" y="500" width="51" height="31"/> + <connections> + <action selector="rightSwitchChanged:" destination="Tha-Wr-sPW" eventType="valueChanged" id="n6V-lL-r8Q"/> + </connections> + </switch> + </subviews> + <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <constraints> + <constraint firstItem="Mnm-Is-oih" firstAttribute="centerY" secondItem="kav-3r-blI" secondAttribute="centerY" id="0NI-PI-ATH"/> + <constraint firstItem="ORA-mb-hJl" firstAttribute="centerY" secondItem="qR7-40-7ir" secondAttribute="centerY" id="7c9-My-TTD"/> + <constraint firstItem="IKu-qh-ksi" firstAttribute="top" secondItem="ORA-mb-hJl" secondAttribute="bottom" constant="8" id="7vb-uB-f1x"/> + <constraint firstItem="Ifm-a9-Wkq" firstAttribute="top" secondItem="qR7-40-7ir" secondAttribute="bottom" constant="7" id="9ET-Wq-Yen"/> + <constraint firstItem="qR7-40-7ir" firstAttribute="top" secondItem="lc1-oP-FNw" secondAttribute="bottom" constant="8" id="A54-NU-I6K"/> + <constraint firstItem="FUU-vZ-jMd" firstAttribute="top" secondItem="znr-YO-4a4" secondAttribute="bottom" constant="17" id="DLv-qJ-h7R"/> + <constraint firstItem="sMF-o9-dZX" firstAttribute="top" secondItem="kav-3r-blI" secondAttribute="bottom" constant="6" id="Fy1-r9-rOd"/> + <constraint firstItem="Ifm-a9-Wkq" firstAttribute="trailing" secondItem="qR7-40-7ir" secondAttribute="trailing" id="Im7-AC-dup"/> + <constraint firstItem="ORA-mb-hJl" firstAttribute="top" secondItem="sMF-o9-dZX" secondAttribute="bottom" constant="8" symbolic="YES" id="K0Z-Kz-m2p"/> + <constraint firstAttribute="trailingMargin" secondItem="qR7-40-7ir" secondAttribute="trailing" id="KZ9-eM-6oW"/> + <constraint firstItem="kav-3r-blI" firstAttribute="leading" secondItem="PPb-b8-nBo" secondAttribute="leadingMargin" id="KrJ-mU-I6l"/> + <constraint firstAttribute="centerX" secondItem="FUU-vZ-jMd" secondAttribute="centerX" id="YGT-a5-Zka"/> + <constraint firstItem="lc1-oP-FNw" firstAttribute="trailing" secondItem="Mnm-Is-oih" secondAttribute="trailing" id="YrP-Jx-D1J"/> + <constraint firstItem="lc1-oP-FNw" firstAttribute="top" secondItem="Mnm-Is-oih" secondAttribute="bottom" constant="6" id="Zsm-LK-7NY"/> + <constraint firstAttribute="trailingMargin" secondItem="Mnm-Is-oih" secondAttribute="trailing" id="bnI-nC-1qS"/> + <constraint firstItem="kav-3r-blI" firstAttribute="top" secondItem="L3F-13-Wf5" secondAttribute="bottom" constant="40" id="eO9-bU-rZY"/> + <constraint firstItem="L3F-13-Wf5" firstAttribute="top" secondItem="znr-YO-4a4" secondAttribute="bottom" constant="300" id="ewm-kv-p8k"/> + <constraint firstItem="IKu-qh-ksi" firstAttribute="leading" secondItem="PPb-b8-nBo" secondAttribute="leadingMargin" id="m9a-Ug-SfK"/> + <constraint firstItem="ORA-mb-hJl" firstAttribute="leading" secondItem="PPb-b8-nBo" secondAttribute="leadingMargin" id="oZO-il-Fd5"/> + <constraint firstItem="sMF-o9-dZX" firstAttribute="leading" secondItem="kav-3r-blI" secondAttribute="leading" id="qtt-KE-YfT"/> + <constraint firstAttribute="centerX" secondItem="L3F-13-Wf5" secondAttribute="centerX" id="zXw-WV-mro"/> + </constraints> + </view> + <navigationItem key="navigationItem" title="PNChart" id="Ukg-Sg-E2z"/> + <connections> + <outlet property="animationsSwitch" destination="Mnm-Is-oih" id="haB-27-exI"/> + <outlet property="centerSwitch" destination="kav-3r-blI" id="aDA-mR-FhR"/> + <outlet property="centerSwitchLabel" destination="sMF-o9-dZX" id="Epf-8v-NeL"/> + <outlet property="changeValueButton" destination="L3F-13-Wf5" id="JnI-y3-Xpj"/> + <outlet property="leftLabel" destination="IKu-qh-ksi" id="CPy-oy-qCP"/> + <outlet property="leftSwitch" destination="ORA-mb-hJl" id="u1M-2f-6P1"/> + <outlet property="rightLabel" destination="Ifm-a9-Wkq" id="QhE-j2-qpe"/> + <outlet property="rightSwitch" destination="qR7-40-7ir" id="noL-6R-d3a"/> + <outlet property="titleLabel" destination="FUU-vZ-jMd" id="dA3-KC-Ht4"/> + </connections> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="kDa-u3-t6i" userLabel="First Responder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="536" y="31"/> + </scene> + <!--PNChart--> + <scene sceneID="pBo-qW-N8B"> + <objects> + <tableViewController id="cc5-BF-Ga2" customClass="PCChartsTableViewController" sceneMemberID="viewController"> + <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="EAk-8x-zLw"> + <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <sections> + <tableViewSection id="Rtv-33-iAC"> + <cells> + <tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" textLabel="dtA-8k-dgn" style="IBUITableViewCellStyleDefault" id="irc-98-Qkg"> + <rect key="frame" x="0.0" y="0.0" width="375" height="44"/> + <autoresizingMask key="autoresizingMask"/> + <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="irc-98-Qkg" id="QVQ-E4-AgU"> + <rect key="frame" x="0.0" y="0.0" width="342" height="43"/> + <autoresizingMask key="autoresizingMask"/> + <subviews> + <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="LineChart" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dtA-8k-dgn"> + <rect key="frame" x="15" y="0.0" width="325" height="43"/> + <autoresizingMask key="autoresizingMask"/> + <fontDescription key="fontDescription" type="system" pointSize="18"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + </subviews> + </tableViewCellContentView> + <connections> + <segue destination="Tha-Wr-sPW" kind="push" identifier="lineChart" id="XHj-XM-h67"/> + </connections> + </tableViewCell> + <tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" textLabel="sGJ-nJ-Evy" style="IBUITableViewCellStyleDefault" id="uiU-qo-XNI"> + <rect key="frame" x="0.0" y="44" width="375" height="44"/> + <autoresizingMask key="autoresizingMask"/> + <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="uiU-qo-XNI" id="QSW-pF-BtE"> + <rect key="frame" x="0.0" y="0.0" width="342" height="43"/> + <autoresizingMask key="autoresizingMask"/> + <subviews> + <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="BarChart" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="sGJ-nJ-Evy"> + <rect key="frame" x="15" y="0.0" width="325" height="43"/> + <autoresizingMask key="autoresizingMask"/> + <fontDescription key="fontDescription" type="system" pointSize="18"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + </subviews> + </tableViewCellContentView> + <connections> + <segue destination="Tha-Wr-sPW" kind="push" identifier="barChart" id="BAm-LA-QbM"/> + </connections> + </tableViewCell> + <tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" textLabel="Bn9-8g-5UP" style="IBUITableViewCellStyleDefault" id="xKf-5i-9cT"> + <rect key="frame" x="0.0" y="88" width="375" height="44"/> + <autoresizingMask key="autoresizingMask"/> + <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="xKf-5i-9cT" id="0Fm-qD-hZk"> + <rect key="frame" x="0.0" y="0.0" width="342" height="43"/> + <autoresizingMask key="autoresizingMask"/> + <subviews> + <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="CircleChart" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Bn9-8g-5UP"> + <rect key="frame" x="15" y="0.0" width="325" height="43"/> + <autoresizingMask key="autoresizingMask"/> + <fontDescription key="fontDescription" type="system" pointSize="18"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + </subviews> + </tableViewCellContentView> + <connections> + <segue destination="Tha-Wr-sPW" kind="push" identifier="circleChart" id="WSA-oe-ed1"/> + </connections> + </tableViewCell> + <tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" textLabel="N5A-vO-fq4" style="IBUITableViewCellStyleDefault" id="1Ha-E5-to7"> + <rect key="frame" x="0.0" y="132" width="375" height="44"/> + <autoresizingMask key="autoresizingMask"/> + <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1Ha-E5-to7" id="3YW-gb-VCd"> + <rect key="frame" x="0.0" y="0.0" width="342" height="43"/> + <autoresizingMask key="autoresizingMask"/> + <subviews> + <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="PieChart" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="N5A-vO-fq4"> + <rect key="frame" x="15" y="0.0" width="325" height="43"/> + <autoresizingMask key="autoresizingMask"/> + <fontDescription key="fontDescription" type="system" pointSize="18"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + </subviews> + </tableViewCellContentView> + <connections> + <segue destination="Tha-Wr-sPW" kind="push" identifier="pieChart" id="pvQ-oy-a9a"/> + </connections> + </tableViewCell> + <tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" indentationLevel="1" indentationWidth="0.0" textLabel="YOU-SK-mQU" style="IBUITableViewCellStyleDefault" id="JJR-oU-C7n"> + <rect key="frame" x="0.0" y="176" width="375" height="44"/> + <autoresizingMask key="autoresizingMask"/> + <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="JJR-oU-C7n" id="iJk-3W-tcy"> + <rect key="frame" x="0.0" y="0.0" width="342" height="43"/> + <autoresizingMask key="autoresizingMask"/> + <subviews> + <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="ScatterChart" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="YOU-SK-mQU"> + <rect key="frame" x="15" y="0.0" width="325" height="43"/> + <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> + <fontDescription key="fontDescription" type="system" pointSize="18"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + </subviews> + </tableViewCellContentView> + <connections> + <segue destination="Tha-Wr-sPW" kind="push" identifier="scatterChart" id="V7s-JV-4Nx"/> + </connections> + </tableViewCell> + <tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" textLabel="Sjk-AS-XhW" style="IBUITableViewCellStyleDefault" id="bev-fA-J4Q"> + <rect key="frame" x="0.0" y="220" width="375" height="44"/> + <autoresizingMask key="autoresizingMask"/> + <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="bev-fA-J4Q" id="nSV-Wu-TAu"> + <rect key="frame" x="0.0" y="0.0" width="342" height="43"/> + <autoresizingMask key="autoresizingMask"/> + <subviews> + <label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="left" text="RadarChart" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Sjk-AS-XhW"> + <rect key="frame" x="15" y="0.0" width="325" height="43"/> + <autoresizingMask key="autoresizingMask"/> + <fontDescription key="fontDescription" type="system" pointSize="18"/> + <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + <nil key="highlightedColor"/> + </label> + </subviews> + </tableViewCellContentView> + <connections> + <segue destination="Tha-Wr-sPW" kind="push" identifier="radarChart" id="4D9-t3-nzn"/> + </connections> + </tableViewCell> + </cells> + </tableViewSection> + </sections> + </tableView> + <navigationItem key="navigationItem" title="PNChart" id="wPu-Ht-gOr"/> + </tableViewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="JMX-8U-h8G" userLabel="First Responder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="50" y="31"/> + </scene> + <!--Navigation Controller--> + <scene sceneID="adz-gG-u3z"> + <objects> + <navigationController definesPresentationContext="YES" id="9Rt-UT-IxH" sceneMemberID="viewController"> + <navigationBar key="navigationBar" contentMode="scaleToFill" id="Vgs-8d-rE2"> + <rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/> + <autoresizingMask key="autoresizingMask"/> + </navigationBar> + <connections> + <segue destination="cc5-BF-Ga2" kind="relationship" relationship="rootViewController" id="Jdg-FU-XfU"/> + </connections> + </navigationController> + <placeholder placeholderIdentifier="IBFirstResponder" id="RN0-OP-dIr" userLabel="First Responder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="-318" y="31"/> + </scene> + </scenes> + <simulatedMetricsContainer key="defaultSimulatedMetrics"> + <simulatedStatusBarMetrics key="statusBar"/> + <simulatedOrientationMetrics key="orientation"/> + <simulatedScreenMetrics key="destination" type="retina4_7.fullscreen"/> + </simulatedMetricsContainer> + <inferredMetricsTieBreakers> + <segue reference="pvQ-oy-a9a"/> + </inferredMetricsTieBreakers> +</document> diff --git a/camerademo/camerademo/Info.plist b/PNChartdemo/PNChartdemo/Info.plist similarity index 100% rename from camerademo/camerademo/Info.plist rename to PNChartdemo/PNChartdemo/Info.plist diff --git a/PNChartdemo/PNChartdemo/PCChartViewController.h b/PNChartdemo/PNChartdemo/PCChartViewController.h new file mode 100755 index 0000000..d8954b3 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PCChartViewController.h @@ -0,0 +1,38 @@ +// +// PCChartViewController.h +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "PNChartDelegate.h" +#import "PNChart.h" + +@interface PCChartViewController : UIViewController<PNChartDelegate> + +@property (nonatomic) PNLineChart * lineChart; +@property (nonatomic) PNBarChart * barChart; +@property (nonatomic) PNCircleChart * circleChart; +@property (nonatomic) PNPieChart *pieChart; +@property (nonatomic) PNScatterChart *scatterChart; +@property (nonatomic) PNRadarChart *radarChart; +@property (weak, nonatomic) IBOutlet UILabel *centerSwitchLabel; + +@property (weak, nonatomic) IBOutlet UILabel *titleLabel; + +- (IBAction)changeValue:(id)sender; +@property (weak, nonatomic) IBOutlet UIButton *changeValueButton; + +@property (weak, nonatomic) IBOutlet UISwitch *animationsSwitch; +@property (weak, nonatomic) IBOutlet UISwitch *leftSwitch; +@property (weak, nonatomic) IBOutlet UISwitch *centerSwitch; +@property (weak, nonatomic) IBOutlet UISwitch *rightSwitch; +@property (weak, nonatomic) IBOutlet UILabel *leftLabel; +@property (weak, nonatomic) IBOutlet UILabel *rightLabel; + +- (IBAction)rightSwitchChanged:(id)sender; +- (IBAction)leftSwitchChanged:(id)sender; + +@end diff --git a/PNChartdemo/PNChartdemo/PCChartViewController.m b/PNChartdemo/PNChartdemo/PCChartViewController.m new file mode 100755 index 0000000..ba59f98 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PCChartViewController.m @@ -0,0 +1,490 @@ +// +// PCChartViewController.m +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PCChartViewController.h" + +#define ARC4RANDOM_MAX 0x100000000 + +@implementation PCChartViewController + + +- (void)viewDidLoad +{ + [super viewDidLoad]; + self.titleLabel.textColor = PNFreshGreen; + self.leftSwitch.hidden = YES; + self.rightSwitch.hidden = YES; + self.leftLabel.hidden = YES; + self.rightLabel.hidden = YES; + self.centerSwitch.hidden = YES; + self.centerSwitchLabel.hidden = YES; + + self.changeValueButton.hidden = YES; + + if ([self.title isEqualToString:@"Line Chart"]) + { + + self.titleLabel.text = @"Line Chart"; + + self.rightSwitch.hidden = NO; + self.rightLabel.hidden = NO; + self.leftSwitch.hidden = NO; + self.leftLabel.hidden = NO; + self.animationsSwitch.hidden = NO; + + self.leftLabel.text = @"Dark Background"; + self.rightLabel.text = @"Show Curved Lines"; + + self.animationsSwitch.enabled = YES; + self.rightSwitch.enabled = YES; + self.leftSwitch.enabled = YES; + [self.animationsSwitch setOn:NO]; + [self.rightSwitch setOn:NO]; + [self.leftSwitch setOn:NO]; + + + self.lineChart.backgroundColor = [UIColor whiteColor]; + self.lineChart.yGridLinesColor = [UIColor grayColor]; + [self.lineChart.chartData enumerateObjectsUsingBlock:^(PNLineChartData *obj, NSUInteger idx, BOOL *stop) { + obj.pointLabelColor = [UIColor blackColor]; + }]; + + self.lineChart = [[PNLineChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 200.0)]; + self.lineChart.showCoordinateAxis = YES; // ��������������������� + self.lineChart.yLabelFormat = @"%1.1f"; //���Y���������float��������������������� + self.lineChart.xLabelFont = [UIFont fontWithName:@"Helvetica-Light" size:8.0]; + [self.lineChart setXLabels:@[@"SEP 1", @"SEP 2", @"SEP 3", @"SEP 4", @"SEP 5", @"SEP 6", @"SEP 7"]]; + self.lineChart.yLabelColor = [UIColor blackColor]; + self.lineChart.xLabelColor = [UIColor blackColor]; + + // added an example to show how yGridLines can be enabled + // the color is set to clearColor so that the demo remains the same + self.lineChart.showGenYLabels = NO; + self.lineChart.showYGridLines = YES; + + //Use yFixedValueMax and yFixedValueMin to Fix the Max and Min Y Value + //Only if you needed + self.lineChart.yFixedValueMax = 300.0; + self.lineChart.yFixedValueMin = 0.0; + + self.lineChart.showSmoothLines = YES; + + [self.lineChart setYLabels:@[ + @"0", + @"50", + @"100", + @"150", + @"200", + @"250", + @"300", + ] + ]; + +// // Line Chart #1 +// NSArray *data01Array = @[@15.1, @60.1, @110.4, @10.0, @186.2, @197.2, @276.2]; +// data01Array = [[data01Array reverseObjectEnumerator] allObjects]; +// PNLineChartData *data01 = [PNLineChartData new]; +// +// data01.rangeColors = @[ +// [[PNLineChartColorRange alloc] initWithRange:NSMakeRange(10, 30) color:[UIColor redColor]], +// [[PNLineChartColorRange alloc] initWithRange:NSMakeRange(100, 200) color:[UIColor purpleColor]] +// ]; +// data01.dataTitle = @"Alpha"; +// data01.color = PNFreshGreen; +// data01.pointLabelColor = [UIColor blackColor]; +// data01.alpha = 0.3f; +// data01.showPointLabel = YES; +// data01.pointLabelFont = [UIFont fontWithName:@"Helvetica-Light" size:9.0]; +// data01.itemCount = data01Array.count; +// data01.inflexionPointColor = PNRed; +// data01.inflexionPointStyle = PNLineChartPointStyleTriangle; +// data01.getData = ^(NSUInteger index) { +// CGFloat yValue = [data01Array[index] floatValue]; +// return [PNLineChartDataItem dataItemWithY:yValue]; +// }; + + // Line Chart #2 + NSArray *data02Array = @[@0.0, @180.1, @26.4, @202.2, @126.2, @167.2, @276.2]; + PNLineChartData *data02 = [PNLineChartData new]; + data02.dataTitle = @"Beta"; + data02.pointLabelColor = [UIColor blackColor]; + data02.color = PNTwitterColor; + data02.alpha = 0.5f; + data02.itemCount = data02Array.count; + data02.inflexionPointStyle = PNLineChartPointStyleCircle; + data02.getData = ^(NSUInteger index) + { + CGFloat yValue = [data02Array[index] floatValue]; + return [PNLineChartDataItem dataItemWithY:yValue]; + }; + + //self.lineChart.chartData = @[data01, data02]; + self.lineChart.chartData = @[data02]; + [self.lineChart.chartData enumerateObjectsUsingBlock:^(PNLineChartData *obj, NSUInteger idx, BOOL *stop) + { + obj.pointLabelColor = [UIColor blackColor]; + }]; + + + [self.lineChart strokeChart]; + self.lineChart.delegate = self; + + + [self.view addSubview:self.lineChart]; + + self.lineChart.legendStyle = PNLegendItemStyleStacked; + self.lineChart.legendFont = [UIFont boldSystemFontOfSize:12.0f]; + self.lineChart.legendFontColor = [UIColor redColor]; + + UIView *legend = [self.lineChart getLegendWithMaxWidth:320]; + [legend setFrame:CGRectMake(30, 340, legend.frame.size.width, legend.frame.size.width)]; + [self.view addSubview:legend]; + } + else if ([self.title isEqualToString:@"Bar Chart"]) + { + static NSNumberFormatter *barChartFormatter; + if (!barChartFormatter) { + barChartFormatter = [[NSNumberFormatter alloc] init]; + barChartFormatter.numberStyle = NSNumberFormatterCurrencyStyle; + barChartFormatter.allowsFloats = NO; + barChartFormatter.maximumFractionDigits = 0; + } + self.titleLabel.text = @"Bar Chart"; + + self.barChart = [[PNBarChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 200.0)]; +// self.barChart.showLabel = NO; + self.barChart.yLabelFormatter = ^(CGFloat yValue) { + return [barChartFormatter stringFromNumber:@(yValue)]; + }; + + self.barChart.yChartLabelWidth = 20.0; + self.barChart.chartMarginLeft = 30.0; + self.barChart.chartMarginRight = 10.0; + self.barChart.chartMarginTop = 5.0; + self.barChart.chartMarginBottom = 10.0; + + + self.barChart.labelMarginTop = 5.0; + self.barChart.showChartBorder = YES; + [self.barChart setXLabels:@[@"2", @"3", @"4", @"5", @"2", @"3", @"4", @"5"]]; +// self.barChart.yLabels = @[@-10,@0,@10]; +// [self.barChart setYValues:@[@10000.0,@30000.0,@10000.0,@100000.0,@500000.0,@1000000.0,@1150000.0,@2150000.0]]; + [self.barChart setYValues:@[@10.82, @1.88, @6.96, @33.93, @10.82, @1.88, @6.96, @33.93]]; + [self.barChart setStrokeColors:@[PNGreen, PNGreen, PNRed, PNGreen, PNGreen, PNGreen, PNRed, PNGreen]]; + self.barChart.isGradientShow = NO; + self.barChart.isShowNumbers = NO; + + [self.barChart strokeChart]; + + self.barChart.delegate = self; + + [self.view addSubview:self.barChart]; + } else if ([self.title isEqualToString:@"Circle Chart"]) { + self.titleLabel.text = @"Circle Chart"; + + + self.circleChart = [[PNCircleChart alloc] initWithFrame:CGRectMake(0, 150.0, SCREEN_WIDTH, 100.0) + total:@100 + current:@60 + clockwise:YES]; + + self.circleChart.backgroundColor = [UIColor whiteColor]; + + [self.circleChart setStrokeColor:[UIColor clearColor]]; + [self.circleChart setStrokeColorGradientStart:[UIColor blueColor]]; + [self.circleChart strokeChart]; + + [self.view addSubview:self.circleChart]; + } else if ([self.title isEqualToString:@"Pie Chart"]) { + self.titleLabel.text = @"Pie Chart"; + self.leftSwitch.hidden = NO; + self.rightSwitch.hidden = NO; + self.leftLabel.hidden = NO; + self.rightLabel.hidden = NO; + self.centerSwitch.hidden = NO; + self.centerSwitchLabel.hidden = NO; + + + NSArray *items = @[[PNPieChartDataItem dataItemWithValue:10 color:PNLightGreen], + [PNPieChartDataItem dataItemWithValue:20 color:PNFreshGreen description:@"WWDC"], + [PNPieChartDataItem dataItemWithValue:40 color:PNDeepGreen description:@"GOOG I/O"], + ]; + + self.pieChart = [[PNPieChart alloc] initWithFrame:CGRectMake((CGFloat) (SCREEN_WIDTH / 2.0 - 100), 135, 200.0, 200.0) items:items]; + self.pieChart.descriptionTextColor = [UIColor whiteColor]; + self.pieChart.descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:11.0]; + self.pieChart.descriptionTextShadowColor = [UIColor clearColor]; + self.pieChart.showAbsoluteValues = NO; + self.pieChart.showOnlyValues = NO; + [self.pieChart strokeChart]; + + + self.pieChart.legendStyle = PNLegendItemStyleStacked; + self.pieChart.legendFont = [UIFont boldSystemFontOfSize:12.0f]; + + UIView *legend = [self.pieChart getLegendWithMaxWidth:200]; + [legend setFrame:CGRectMake(130, 350, legend.frame.size.width, legend.frame.size.height)]; + [self.view addSubview:legend]; + + [self.view addSubview:self.pieChart]; + self.changeValueButton.hidden = YES; + } else if ([self.title isEqualToString:@"Scatter Chart"]) { + self.animationsSwitch.hidden = YES; + + self.titleLabel.text = @"Scatter Chart"; + + self.scatterChart = [[PNScatterChart alloc] initWithFrame:CGRectMake((CGFloat) (SCREEN_WIDTH / 6.0 - 30), 135, 280, 200)]; +// self.scatterChart.yLabelFormat = @"xxx %1.1f"; + [self.scatterChart setAxisXWithMinimumValue:20 andMaxValue:100 toTicks:6]; + [self.scatterChart setAxisYWithMinimumValue:30 andMaxValue:50 toTicks:5]; + [self.scatterChart setAxisXLabel:@[@"x1", @"x2", @"x3", @"x4", @"x5", @"x6"]]; + [self.scatterChart setAxisYLabel:@[@"y1", @"y2", @"y3", @"y4", @"y5"]]; + + NSArray *data01Array = [self randomSetOfObjects]; + PNScatterChartData *data01 = [PNScatterChartData new]; + data01.strokeColor = PNGreen; + data01.fillColor = PNFreshGreen; + data01.size = 2; + data01.itemCount = [data01Array[0] count]; + data01.inflexionPointStyle = PNScatterChartPointStyleCircle; + __block NSMutableArray *XAr1 = [NSMutableArray arrayWithArray:data01Array[0]]; + __block NSMutableArray *YAr1 = [NSMutableArray arrayWithArray:data01Array[1]]; + + data01.getData = ^(NSUInteger index) { + CGFloat xValue; + xValue = [XAr1[index] floatValue]; + CGFloat yValue = [YAr1[index] floatValue]; + return [PNScatterChartDataItem dataItemWithX:xValue AndWithY:yValue]; + }; + + [self.scatterChart setup]; + self.scatterChart.chartData = @[data01]; +/*** + this is for drawing line to compare + CGPoint start = CGPointMake(20, 35); + CGPoint end = CGPointMake(80, 45); + [self.scatterChart drawLineFromPoint:start ToPoint:end WithLineWith:2 AndWithColor:PNBlack]; +***/ + self.scatterChart.delegate = self; + self.changeValueButton.hidden = YES; + [self.view addSubview:self.scatterChart]; + } else if ([self.title isEqualToString:@"Radar Chart"]) { + self.titleLabel.text = @"Radar Chart"; + + self.leftSwitch.hidden = NO; + self.rightSwitch.hidden = NO; + self.leftLabel.hidden = NO; + self.rightLabel.hidden = NO; + self.leftLabel.text = @"Labels Style"; + self.rightLabel.text = @"Graduation"; + + + NSArray *items = @[[PNRadarChartDataItem dataItemWithValue:3 description:@"Art"], + [PNRadarChartDataItem dataItemWithValue:2 description:@"Math"], + [PNRadarChartDataItem dataItemWithValue:8 description:@"Sports"], + [PNRadarChartDataItem dataItemWithValue:5 description:@"Literature"], + [PNRadarChartDataItem dataItemWithValue:4 description:@"Other"], + ]; + self.radarChart = [[PNRadarChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 300.0) items:items valueDivider:1]; + + self.radarChart.plotColor = [UIColor redColor]; + + [self.radarChart strokeChart]; + + [self.view addSubview:self.radarChart]; + } + +} + + +- (void)userClickedOnLineKeyPoint:(CGPoint)point lineIndex:(NSInteger)lineIndex pointIndex:(NSInteger)pointIndex { + NSLog(@"Click Key on line %f, %f line index is %d and point index is %d", point.x, point.y, (int) lineIndex, (int) pointIndex); +} + +- (void)userClickedOnLinePoint:(CGPoint)point lineIndex:(NSInteger)lineIndex { + NSLog(@"Click on line %f, %f, line index is %d", point.x, point.y, (int) lineIndex); +} + + +- (IBAction)changeValue:(id)sender { + + if ([self.title isEqualToString:@"Line Chart"]) { + + // Line Chart #1 + NSArray *data01Array = @[@(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300)]; + PNLineChartData *data01 = [PNLineChartData new]; + data01.color = PNFreshGreen; + data01.itemCount = data01Array.count; + data01.inflexionPointColor = PNRed; + data01.inflexionPointStyle = PNLineChartPointStyleTriangle; + data01.getData = ^(NSUInteger index) { + CGFloat yValue = [data01Array[index] floatValue]; + return [PNLineChartDataItem dataItemWithY:yValue]; + }; + + // Line Chart #2 + NSArray *data02Array = @[@(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300)]; + PNLineChartData *data02 = [PNLineChartData new]; + data02.color = PNTwitterColor; + data02.itemCount = data02Array.count; + data02.inflexionPointStyle = PNLineChartPointStyleSquare; + data02.getData = ^(NSUInteger index) { + CGFloat yValue = [data02Array[index] floatValue]; + return [PNLineChartDataItem dataItemWithY:yValue]; + }; + + [self.lineChart setXLabels:@[@"DEC 1", @"DEC 2", @"DEC 3", @"DEC 4", @"DEC 5", @"DEC 6", @"DEC 7"]]; + [self.lineChart updateChartData:@[data01, data02]]; + + } else if ([self.title isEqualToString:@"Bar Chart"]) { + [self.barChart setXLabels:@[@"Jan 1", @"Jan 2", @"Jan 3", @"Jan 4", @"Jan 5", @"Jan 6", @"Jan 7"]]; + [self.barChart updateChartData:@[@(arc4random() % 30), @(arc4random() % 30), @(arc4random() % 30), @(arc4random() % 30), @(arc4random() % 30), @(arc4random() % 30), @(arc4random() % 30)]]; + } else if ([self.title isEqualToString:@"Circle Chart"]) { + [self.circleChart updateChartByCurrent:@(arc4random() % 100)]; + } else if ([self.title isEqualToString:@"Scatter Chart"]) { + // will be code soon. + } + +} + +- (void)userClickedOnBarAtIndex:(NSInteger)barIndex { + + NSLog(@"Click on bar %@", @(barIndex)); + + PNBar *bar = self.barChart.bars[(NSUInteger) barIndex]; + + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; + + animation.fromValue = @1.0; + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + animation.toValue = @1.1; + animation.duration = 0.2; + animation.repeatCount = 0; + animation.autoreverses = YES; + animation.removedOnCompletion = YES; + animation.fillMode = kCAFillModeForwards; + + [bar.layer addAnimation:animation forKey:@"Float"]; +} + +/* this function is used only for creating random points */ +- (NSArray *)randomSetOfObjects { + NSMutableArray *array = [NSMutableArray array]; + NSString *LabelFormat = @"%1.f"; + NSMutableArray *XAr = [NSMutableArray array]; + NSMutableArray *YAr = [NSMutableArray array]; + for (int i = 0; i < 25; i++) { + [XAr addObject:[NSString stringWithFormat:LabelFormat, (((double) arc4random() / ARC4RANDOM_MAX) * (self.scatterChart.AxisX_maxValue - self.scatterChart.AxisX_minValue) + self.scatterChart.AxisX_minValue)]]; + [YAr addObject:[NSString stringWithFormat:LabelFormat, (((double) arc4random() / ARC4RANDOM_MAX) * (self.scatterChart.AxisY_maxValue - self.scatterChart.AxisY_minValue) + self.scatterChart.AxisY_minValue)]]; + } + [array addObject:XAr]; + [array addObject:YAr]; + return array; +} + +- (IBAction)rightSwitchChanged:(id)sender +{ + if ([self.title isEqualToString:@"Pie Chart"]) { + UISwitch *showLabels = (UISwitch *) sender; + self.pieChart.showOnlyValues = !showLabels.on; + [self.pieChart strokeChart]; + } + if ([self.title isEqualToString:@"Radar Chart"]) { + UISwitch *showLabels = (UISwitch *) sender; + self.radarChart.isShowGraduation = !showLabels.on; + [self.radarChart strokeChart]; + } else if ([self.title isEqualToString:@"Line Chart"]) + { + UISwitch *showLabels = (UISwitch *) sender; + self.lineChart.showSmoothLines = showLabels.on; + NSLog(@"self.lineChart.showSmoothLines : %d", self.lineChart.showSmoothLines); + [self.lineChart strokeChart]; + } +} + +- (IBAction)centerSwitchChanged:(id)sender { + if (self.pieChart) { + [self.pieChart setEnableMultipleSelection:self.centerSwitch.on]; + [self.pieChart strokeChart]; + } + +} + +- (IBAction)leftSwitchChanged:(id)sender { + if ([self.title isEqualToString:@"Pie Chart"]) { + UISwitch *showRelative = (UISwitch *) sender; + self.pieChart.showAbsoluteValues = !showRelative.on; + [self.pieChart strokeChart]; + } else if ([self.title isEqualToString:@"Radar Chart"]) { + UISwitch *showRelative = (UISwitch *) sender; + if (showRelative.on) { + self.radarChart.labelStyle = PNRadarChartLabelStyleHorizontal; + } else { + self.radarChart.labelStyle = PNRadarChartLabelStyleCircle; + } + [self.radarChart strokeChart]; + + } else if ([self.title isEqualToString:@"Line Chart"]) { + UISwitch *senderSwitch = (UISwitch *) sender; + if (senderSwitch.isOn) { + UIColor *lineChartLabelColor = [UIColor cyanColor]; + UIColor *darkBackgroundColor = [UIColor colorWithRed:0.47 green:0.47 blue:0.47 alpha:1.0]; + + UIColor *gridLinesForDarkBackgroundColor = [UIColor colorWithRed:242 / 255.0 green:242 / 255.0 blue:242 / 255.0 alpha:1.0]; + self.lineChart.backgroundColor = darkBackgroundColor; + self.lineChart.yGridLinesColor = gridLinesForDarkBackgroundColor; + self.lineChart.showYGridLines = YES; + self.lineChart.yLabelColor = lineChartLabelColor; + self.lineChart.xLabelColor = lineChartLabelColor; + [self.lineChart.chartData enumerateObjectsUsingBlock:^(PNLineChartData *obj, NSUInteger idx, BOOL *stop) { + obj.pointLabelColor = lineChartLabelColor; + }]; + } else { + self.lineChart.backgroundColor = [UIColor whiteColor]; + self.lineChart.yGridLinesColor = [UIColor grayColor]; + self.lineChart.yLabelColor = [UIColor blackColor]; + self.lineChart.xLabelColor = [UIColor blackColor]; + [self.lineChart.chartData enumerateObjectsUsingBlock:^(PNLineChartData *obj, NSUInteger idx, BOOL *stop) { + obj.pointLabelColor = [UIColor blackColor]; + }]; + } + [self.lineChart setXLabels:@[@"DEC 1", @"DEC 2", @"DEC 3", @"DEC 4", @"DEC 5", @"DEC 6", @"DEC 7"]]; + [self.lineChart setYLabels:@[ + @"0 min", + @"50 min", + @"100 min", + @"150 min", + @"200 min", + @"250 min", + @"300 min", + ] + ]; + [self.lineChart strokeChart]; + } +} + +- (IBAction)animationsSwitchChanged:(UISwitch *)sender { + if ([self.title isEqualToString:@"Circle Chart"]) { + self.circleChart.displayAnimated = sender.on; + [self.circleChart strokeChart]; + } else if ([self.title isEqualToString:@"Line Chart"]) { + self.lineChart.displayAnimated = sender.on; + [self.lineChart strokeChart]; + } else if ([self.title isEqualToString:@"Bar Chart"]) { + self.barChart.displayAnimated = sender.on; + [self.barChart strokeChart]; + } else if ([self.title isEqualToString:@"Pie Chart"]) { + self.pieChart.displayAnimated = sender.on; + [self.pieChart strokeChart]; + } else if ([self.title isEqualToString:@"Radar Chart"]) { + self.radarChart.displayAnimated = sender.on; + [self.radarChart strokeChart]; + } +} + +@end diff --git a/PNChartdemo/PNChartdemo/PCChartsTableViewController.h b/PNChartdemo/PNChartdemo/PCChartsTableViewController.h new file mode 100755 index 0000000..bc25f8e --- /dev/null +++ b/PNChartdemo/PNChartdemo/PCChartsTableViewController.h @@ -0,0 +1,13 @@ +// +// PCChartsTableViewController.h +// PNChartDemo +// +// Created by kevinzhow on 13-12-1. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> + +@interface PCChartsTableViewController : UITableViewController + +@end diff --git a/PNChartdemo/PNChartdemo/PCChartsTableViewController.m b/PNChartdemo/PNChartdemo/PCChartsTableViewController.m new file mode 100755 index 0000000..39d923e --- /dev/null +++ b/PNChartdemo/PNChartdemo/PCChartsTableViewController.m @@ -0,0 +1,56 @@ +// +// PCChartsTableViewController.m +// PNChartDemo +// +// Created by kevinzhow on 13-12-1. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PCChartsTableViewController.h" + +@implementation PCChartsTableViewController + +// In a story board-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender +{ + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. + + UIViewController * viewController = [segue destinationViewController]; + + if ([segue.identifier isEqualToString:@"lineChart"]) { + + //Add line chart + + viewController.title = @"Line Chart"; + + } else if ([segue.identifier isEqualToString:@"barChart"]) + { + //Add bar chart + + viewController.title = @"Bar Chart"; + } else if ([segue.identifier isEqualToString:@"circleChart"]) + { + //Add circle chart + + viewController.title = @"Circle Chart"; + + } else if ([segue.identifier isEqualToString:@"pieChart"]) + { + //Add pie chart + + viewController.title = @"Pie Chart"; + } else if ([segue.identifier isEqualToString:@"scatterChart"]) + { + //Add scatter chart + + viewController.title = @"Scatter Chart"; + }else if ([segue.identifier isEqualToString:@"radarChart"]) + { + //Add radar chart + + viewController.title = @"Radar Chart"; + } +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNBar.h b/PNChartdemo/PNChartdemo/PNChart/PNBar.h new file mode 100755 index 0000000..68e6b0b --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNBar.h @@ -0,0 +1,37 @@ +// +// PNBar.h +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import <QuartzCore/QuartzCore.h> + +@interface PNBar : UIView + + +- (void)rollBack; + +@property (nonatomic) float grade; +@property (nonatomic) float maxDivisor; + +@property (nonatomic) CAShapeLayer *chartLine; +@property (nonatomic) UIColor *barColor; +@property (nonatomic) UIColor *barColorGradientStart; +@property (nonatomic) CGFloat barRadius; +@property (nonatomic) CAShapeLayer *gradientMask; + +@property (nonatomic) CAShapeLayer *gradeLayer; +@property (nonatomic) CATextLayer* textLayer; + +/** Text color for all bars in the chart. */ +@property (nonatomic) UIColor * labelTextColor; + +@property (nonatomic, assign) BOOL isNegative; //!< ��������������� +@property (nonatomic, assign) BOOL isShowNumber; //!< ������������numbers + +/** Display the bar with or without animation. Default is YES. **/ +@property (nonatomic) BOOL displayAnimated; +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNBar.m b/PNChartdemo/PNChartdemo/PNChart/PNBar.m new file mode 100755 index 0000000..8ea5fc9 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNBar.m @@ -0,0 +1,288 @@ +// +// PNBar.m +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNBar.h" +#import "PNColor.h" +#import <CoreText/CoreText.h> + +@interface PNBar () + +@property (nonatomic) float copyGrade; + +@end + +@implementation PNBar + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + _chartLine = [CAShapeLayer layer]; + _chartLine.lineCap = kCALineCapButt; + _chartLine.fillColor = [[UIColor whiteColor] CGColor]; + _chartLine.lineWidth = self.frame.size.width; + _chartLine.strokeEnd = 0.0; + self.clipsToBounds = YES; + [self.layer addSublayer:_chartLine]; + self.barRadius = 2.0; + } + + return self; +} + +-(void)setBarRadius:(CGFloat)barRadius +{ + _barRadius = barRadius; + self.layer.cornerRadius = _barRadius; +} + + +- (void)setGrade:(float)grade +{ + _copyGrade = grade; + CGFloat startPosY = (1 - grade) * self.frame.size.height; + + UIBezierPath *progressline = [UIBezierPath bezierPath]; + + [progressline moveToPoint:CGPointMake(self.frame.size.width / 2.0, self.frame.size.height)]; + [progressline addLineToPoint:CGPointMake(self.frame.size.width / 2.0, startPosY)]; + + [progressline setLineWidth:1.0]; + [progressline setLineCapStyle:kCGLineCapSquare]; + [self addAnimationIfNeededWithProgressLine:progressline]; + + + if (_barColor) { + _chartLine.strokeColor = [_barColor CGColor]; + } + else { + _chartLine.strokeColor = [PNGreen CGColor]; + } + + if (_grade) { + + _chartLine.path = progressline.CGPath; + + if (_barColorGradientStart) { + + // Add gradient + self.gradientMask.path = progressline.CGPath; + + CABasicAnimation* opacityAnimation = [self fadeAnimation]; + [self.textLayer addAnimation:opacityAnimation forKey:nil]; + + } + + }else{ + _chartLine.strokeEnd = 1.0; + + _chartLine.path = progressline.CGPath; + // Check if user wants to add a gradient from the start color to the bar color + if (_barColorGradientStart) { + + // Add gradient + self.gradientMask = [CAShapeLayer layer]; + self.gradientMask.fillColor = [[UIColor clearColor] CGColor]; + self.gradientMask.strokeColor = [[UIColor blackColor] CGColor]; + self.gradientMask.lineWidth = self.frame.size.width; + self.gradientMask.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height); + self.gradientMask.path = progressline.CGPath; + + CAGradientLayer *gradientLayer = [CAGradientLayer layer]; + gradientLayer.startPoint = CGPointMake(0.0,0.0); + gradientLayer.endPoint = CGPointMake(1.0 ,0.0); + gradientLayer.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height); + UIColor *middleColor = [UIColor colorWithWhite:255/255 alpha:0.8]; + NSArray *colors = @[ + (__bridge id)self.barColor.CGColor, + (__bridge id)middleColor.CGColor, + (__bridge id)self.barColor.CGColor + ]; + gradientLayer.colors = colors; + + [gradientLayer setMask:self.gradientMask]; + + [_chartLine addSublayer:gradientLayer]; + + self.gradientMask.strokeEnd = 1.0; + + CABasicAnimation* opacityAnimation = [self fadeAnimation]; + [self.textLayer addAnimation:opacityAnimation forKey:nil]; + } + } + + _grade = grade; + +} + + +- (void)rollBack +{ + [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations: ^{ + _chartLine.strokeColor = [UIColor clearColor].CGColor; + } completion:nil]; +} + +- (void)setBarColorGradientStart:(UIColor *)barColorGradientStart +{ + // Set gradient color, remove any existing sublayer first + for (CALayer *sublayer in [_chartLine sublayers]) { + [sublayer removeFromSuperlayer]; + } + _barColorGradientStart = barColorGradientStart; + + [self setGrade:_grade]; + +} + +// Only override drawRect: if you perform custom drawing. +// An empty implementation adversely affects performance during animation. +- (void)drawRect:(CGRect)rect +{ + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor); + CGContextFillRect(context, rect); +} + + +// add number display on the top of bar +-(CGPathRef)gradePath:(CGRect)rect +{ + return nil; +} + +-(CATextLayer*)textLayer +{ + if (!_textLayer) { + _textLayer = [[CATextLayer alloc]init]; + [_textLayer setString:@"0"]; + [_textLayer setAlignmentMode:kCAAlignmentCenter]; + [_textLayer setForegroundColor:[_labelTextColor CGColor]]; + _textLayer.hidden = YES; + + } + + return _textLayer; +} + +- (void) setLabelTextColor:(UIColor *)labelTextColor { + _labelTextColor = labelTextColor; + [_textLayer setForegroundColor:[_labelTextColor CGColor]]; +} + +-(void)setGradeFrame:(CGFloat)grade startPosY:(CGFloat)startPosY +{ + CGFloat textheigt = self.bounds.size.height*self.grade; + + CGFloat topSpace = self.bounds.size.height * (1-self.grade); + CGFloat textWidth = self.bounds.size.width; + + [_chartLine addSublayer:self.textLayer]; + [self.textLayer setFontSize:18.0]; + + [self.textLayer setString:[[NSString alloc]initWithFormat:@"%0.f",grade*self.maxDivisor]]; + + CGSize size = CGSizeMake(320,2000); //������������������������ + NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:18.0]}; + size = [self.textLayer.string boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size; + float verticalY ; + + if (size.height>=textheigt) { + + verticalY = topSpace - size.height; + } else { + verticalY = topSpace + (textheigt-size.height)/2.0; + } + + [self.textLayer setFrame:CGRectMake((textWidth-size.width)/2.0,verticalY, size.width,size.height)]; + self.textLayer.contentsScale = [UIScreen mainScreen].scale; + +} + +- (void)setIsShowNumber:(BOOL)isShowNumber{ + if (isShowNumber) { + self.textLayer.hidden = NO; + [self setGradeFrame:_copyGrade startPosY:0]; + }else{ + self.textLayer.hidden = YES; + } +} +- (void)setIsNegative:(BOOL)isNegative{ + if (isNegative) { + [self.textLayer setString:[[NSString alloc]initWithFormat:@"- %1.f",_grade*self.maxDivisor]]; + + CGSize size = CGSizeMake(320,2000); //������������������������ + NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:18.0]}; + size = [self.textLayer.string boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size; + CGRect frame = self.textLayer.frame; + frame.origin.x = (self.bounds.size.width - size.width)/2.0; + frame.size = size; + self.textLayer.frame = frame; + + [self addRotationAnimationIfNeeded]; + } +} + +-(CABasicAnimation*)fadeAnimation +{ + CABasicAnimation* fadeAnimation = nil; + if (self.displayAnimated) { + fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + fadeAnimation.fromValue = [NSNumber numberWithFloat:0.0]; + fadeAnimation.toValue = [NSNumber numberWithFloat:1.0]; + fadeAnimation.duration = 2.0; + } + return fadeAnimation; +} + +-(void)addAnimationIfNeededWithProgressLine:(UIBezierPath *)progressline +{ + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = nil; + + if (_grade) { + pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; + pathAnimation.fromValue = (id)_chartLine.path; + pathAnimation.toValue = (id)[progressline CGPath]; + pathAnimation.duration = 0.5f; + pathAnimation.autoreverses = NO; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + [_chartLine addAnimation:pathAnimation forKey:@"animationKey"]; + } + else { + pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathAnimation.duration = 1.0; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @0.0f; + pathAnimation.toValue = @1.0f; + [_chartLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + } + + [self.gradientMask addAnimation:pathAnimation forKey:@"animationKey"]; + } +} + +- (void)addRotationAnimationIfNeeded +{ + if (self.displayAnimated) { + CABasicAnimation* rotationAnimation; + rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; + rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI]; + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + rotationAnimation.duration = 0.1; + rotationAnimation.repeatCount = 0;//������������������������������������ + rotationAnimation.cumulative = NO; + rotationAnimation.removedOnCompletion = NO; + rotationAnimation.fillMode = kCAFillModeForwards; + [self.textLayer addAnimation:rotationAnimation forKey:@"Rotation"]; + } +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNBarChart.h b/PNChartdemo/PNChartdemo/PNChart/PNBarChart.h new file mode 100755 index 0000000..e628c08 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNBarChart.h @@ -0,0 +1,123 @@ +// +// PNBarChart.h +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "PNGenericChart.h" +#import "PNChartDelegate.h" +#import "PNBar.h" + +#define kXLabelMargin 15 +#define kYLabelMargin 15 +#define kYLabelHeight 11 +#define kXLabelHeight 20 + +typedef NSString *(^PNYLabelFormatter)(CGFloat yLabelValue); + +@interface PNBarChart : PNGenericChart + +/** + * Draws the chart in an animated fashion. + */ +- (void)strokeChart; + +@property (nonatomic) NSArray *xLabels; +@property (nonatomic) NSArray *yLabels; +@property (nonatomic) NSArray *yValues; + +@property (nonatomic) NSMutableArray * bars; + +@property (nonatomic) CGFloat xLabelWidth; +@property (nonatomic) float yValueMax; +@property (nonatomic) UIColor *strokeColor; +@property (nonatomic) NSArray *strokeColors; + + +/** Update Values. */ +- (void)updateChartData:(NSArray *)data; + +/** Changes chart margin. */ +@property (nonatomic) CGFloat yChartLabelWidth; + +/** Formats the ylabel text. */ +@property (copy) PNYLabelFormatter yLabelFormatter; + +/** Prefix to y label values, none if unset. */ +@property (nonatomic) NSString *yLabelPrefix; + +/** Suffix to y label values, none if unset. */ +@property (nonatomic) NSString *yLabelSuffix; + +@property (nonatomic) CGFloat chartMarginLeft; +@property (nonatomic) CGFloat chartMarginRight; +@property (nonatomic) CGFloat chartMarginTop; +@property (nonatomic) CGFloat chartMarginBottom; + +/** Controls whether labels should be displayed. */ +@property (nonatomic) BOOL showLabel; + +/** Controls whether the chart border line should be displayed. */ +@property (nonatomic) BOOL showChartBorder; + +@property (nonatomic) UIColor *chartBorderColor; + +/** Controls whether the chart Horizontal separator should be displayed. */ +@property (nonatomic, assign) BOOL showLevelLine; + +/** Chart bottom border, co-linear with the x-axis. */ +@property (nonatomic) CAShapeLayer * chartBottomLine; + +/** Chart bottom border, level separator-linear with the x-axis. */ +@property (nonatomic) CAShapeLayer * chartLevelLine; + +/** Chart left border, co-linear with the y-axis. */ +@property (nonatomic) CAShapeLayer * chartLeftLine; + +/** Corner radius for all bars in the chart. */ +@property (nonatomic) CGFloat barRadius; + +/** Width of all bars in the chart. */ +@property (nonatomic) CGFloat barWidth; + +@property (nonatomic) CGFloat labelMarginTop; + +/** Background color of all bars in the chart. */ +@property (nonatomic) UIColor * barBackgroundColor; + +/** Text color for all bars in the chart. */ +@property (nonatomic) UIColor * labelTextColor; + +/** Font for all bars in the chart. */ +@property (nonatomic) UIFont * labelFont; + +/** How many labels on the x-axis to skip in between displaying labels. */ +@property (nonatomic) NSInteger xLabelSkip; + +/** How many labels on the y-axis to skip in between displaying labels. */ +@property (nonatomic) NSInteger yLabelSum; + +/** The maximum for the range of values to display on the y-axis. */ +@property (nonatomic) CGFloat yMaxValue; + +/** The minimum for the range of values to display on the y-axis. */ +@property (nonatomic) CGFloat yMinValue; + +/** Controls whether each bar should have a gradient fill. */ +@property (nonatomic) UIColor *barColorGradientStart; + +/** Controls whether text for x-axis be straight or rotate 45 degree. */ +@property (nonatomic) BOOL rotateForXAxisText; + +@property (nonatomic, weak) id<PNChartDelegate> delegate; + +/**whether show gradient bar*/ +@property (nonatomic, assign) BOOL isGradientShow; + +/** whether show numbers*/ +@property (nonatomic, assign) BOOL isShowNumbers; + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNBarChart.m b/PNChartdemo/PNChartdemo/PNChart/PNBarChart.m new file mode 100755 index 0000000..50e035c --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNBarChart.m @@ -0,0 +1,459 @@ +// +// PNBarChart.m +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNBarChart.h" +#import "PNColor.h" +#import "PNChartLabel.h" + +@interface PNBarChart () { + NSMutableArray *_xChartLabels; + NSMutableArray *_yChartLabels; +} + +- (UIColor *)barColorAtIndex:(NSUInteger)index; + +@end + +@implementation PNBarChart + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + + if (self) { + [self setupDefaultValues]; + } + return self; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + [self setupDefaultValues]; + } + + return self; +} + +- (void)setupDefaultValues +{ + [super setupDefaultValues]; + self.backgroundColor = [UIColor whiteColor]; + self.clipsToBounds = YES; + _showLabel = YES; + _barBackgroundColor = PNLightGrey; + _labelTextColor = [UIColor grayColor]; + _labelFont = [UIFont systemFontOfSize:11.0f]; + _xChartLabels = [NSMutableArray array]; + _yChartLabels = [NSMutableArray array]; + _bars = [NSMutableArray array]; + _xLabelSkip = 1; + _yLabelSum = 4; + _labelMarginTop = 2; + _chartMarginLeft = 25.0; + _chartMarginRight = 25.0; + _chartMarginTop = 25.0; + _chartMarginBottom = 25.0; + _barRadius = 2.0; + _showChartBorder = NO; + _chartBorderColor = PNLightGrey; + _showLevelLine = NO; + _yChartLabelWidth = 18; + _rotateForXAxisText = false; + _isGradientShow = YES; + _isShowNumbers = YES; + _yLabelPrefix = @""; + _yLabelSuffix = @""; + _yLabelFormatter = ^(CGFloat yValue){ + return [NSString stringWithFormat:@"%1.f",yValue]; + }; +} + +- (void)setYValues:(NSArray *)yValues +{ + _yValues = yValues; + //make the _yLabelSum value dependant of the distinct values of yValues to avoid duplicates on yAxis + + if (_showLabel) { + [self __addYCoordinateLabelsValues]; + } else { + [self processYMaxValue]; + } +} + +- (void)processYMaxValue { + NSArray *yAxisValues = _yLabels ? _yLabels : _yValues; + _yLabelSum = _yLabels ? _yLabels.count - 1 :_yLabelSum; + if (_yMaxValue) { + _yValueMax = _yMaxValue; + } else { + [self getYValueMax:yAxisValues]; + } + + if (_yLabelSum==4) { + _yLabelSum = yAxisValues.count; + (_yLabelSum % 2 == 0) ? _yLabelSum : _yLabelSum++; + } +} + +#pragma mark - Private Method +#pragma mark - Add Y Label +- (void)__addYCoordinateLabelsValues{ + + [self viewCleanupForCollection:_yChartLabels]; + + [self processYMaxValue]; + + float sectionHeight = (self.frame.size.height - _chartMarginTop - _chartMarginBottom - kXLabelHeight) / _yLabelSum; + + for (int i = 0; i <= _yLabelSum; i++) { + NSString *labelText; + if (_yLabels) { + float yAsixValue = [_yLabels[_yLabels.count - i - 1] floatValue]; + labelText= _yLabelFormatter(yAsixValue); + } else { + labelText = _yLabelFormatter((float)_yValueMax * ( (_yLabelSum - i) / (float)_yLabelSum )); + } + + PNChartLabel *label = [[PNChartLabel alloc] initWithFrame:CGRectZero]; + label.font = _labelFont; + label.textColor = _labelTextColor; + [label setTextAlignment:NSTextAlignmentRight]; + label.text = [NSString stringWithFormat:@"%@%@%@", _yLabelPrefix, labelText, _yLabelSuffix]; + + [self addSubview:label]; + + label.frame = (CGRect){0, sectionHeight * i + _chartMarginTop - kYLabelHeight/2.0 + kXLabelHeight + _labelMarginTop, _yChartLabelWidth, kYLabelHeight}; + + [_yChartLabels addObject:label]; + } +} + +-(void)updateChartData:(NSArray *)data{ + self.yValues = data; + [self updateBar]; +} + +- (void)getYValueMax:(NSArray *)yLabels +{ + CGFloat max = [[yLabels valueForKeyPath:@"@max.floatValue"] floatValue]; + + //ensure max is even + _yValueMax = max ; + + if (_yValueMax == 0) { + _yValueMax = _yMinValue; + } +} + +- (void)setXLabels:(NSArray *)xLabels +{ + _xLabels = xLabels; + + if (_xChartLabels) { + [self viewCleanupForCollection:_xChartLabels]; + }else{ + _xChartLabels = [NSMutableArray new]; + } + + _xLabelWidth = (self.frame.size.width - _chartMarginLeft - _chartMarginRight) / [xLabels count]; + + if (_showLabel) { + int labelAddCount = 0; + for (int index = 0; index < _xLabels.count; index++) { + labelAddCount += 1; + + if (labelAddCount == _xLabelSkip) { + NSString *labelText = [_xLabels[index] description]; + PNChartLabel * label = [[PNChartLabel alloc] initWithFrame:CGRectMake(0, 0, _xLabelWidth, kXLabelHeight)]; + label.font = _labelFont; + label.textColor = _labelTextColor; + [label setTextAlignment:NSTextAlignmentCenter]; + label.text = labelText; + //[label sizeToFit]; + CGFloat labelXPosition; + if (_rotateForXAxisText){ + label.transform = CGAffineTransformMakeRotation(M_PI / 4); + labelXPosition = (index * _xLabelWidth + _chartMarginLeft + _xLabelWidth /1.5); + } + else{ + labelXPosition = (index * _xLabelWidth + _chartMarginLeft + _xLabelWidth /2.0 ); + } + label.center = CGPointMake(labelXPosition, + self.frame.size.height - _chartMarginTop + label.frame.size.height /2.0 + _labelMarginTop); + labelAddCount = 0; + + [_xChartLabels addObject:label]; + [self addSubview:label]; + } + } + } +} + + +- (void)setStrokeColor:(UIColor *)strokeColor +{ + _strokeColor = strokeColor; +} + +- (void)updateBar +{ + + //Add bars + CGFloat chartCavanHeight = self.frame.size.height - _chartMarginTop - _chartMarginBottom - kXLabelHeight; + NSInteger index = 0; + + for (NSNumber *valueString in _yValues) { + + PNBar *bar; + + if (_bars.count == _yValues.count) { + bar = [_bars objectAtIndex:index]; + }else{ + CGFloat barWidth; + CGFloat barXPosition; + + if (_barWidth) { + barWidth = _barWidth; + barXPosition = index * _xLabelWidth + _chartMarginLeft + _xLabelWidth /2.0 - _barWidth /2.0; + }else{ + barXPosition = index * _xLabelWidth + _chartMarginLeft + _xLabelWidth * 0.25; + if (_showLabel) { + barWidth = _xLabelWidth * 0.5; + + } + else { + barWidth = _xLabelWidth * 0.6; + + } + } + + bar = [[PNBar alloc] initWithFrame:CGRectMake(barXPosition, //Bar X position + self.frame.size.height - chartCavanHeight - kXLabelHeight - _chartMarginBottom + _chartMarginTop , //Bar Y position + barWidth, // Bar witdh + self.showLevelLine ? chartCavanHeight/2.0:chartCavanHeight)]; //Bar height + + //Change Bar Radius + bar.barRadius = _barRadius; + + //Set Bar Animation + bar.displayAnimated = self.displayAnimated; + + //Change Bar Background color + bar.backgroundColor = _barBackgroundColor; + //Bar StrokColor First + if (self.strokeColor) { + bar.barColor = self.strokeColor; + }else{ + bar.barColor = [self barColorAtIndex:index]; + } + + if (self.labelTextColor) { + bar.labelTextColor = self.labelTextColor; + } + + // Add gradient + if (self.isGradientShow) { + bar.barColorGradientStart = bar.barColor; + } + + //For Click Index + bar.tag = index; + + [_bars addObject:bar]; + [self addSubview:bar]; + } + + //Height Of Bar + float value = [valueString floatValue]; + float grade =fabsf((float)value / (float)_yValueMax); + + if (isnan(grade)) { + grade = 0; + } + bar.maxDivisor = (float)_yValueMax; + bar.grade = grade; + bar.isShowNumber = self.isShowNumbers; + CGRect originalFrame = bar.frame; + NSString *currentNumber = [NSString stringWithFormat:@"%f",value]; + + if ([[currentNumber substringToIndex:1] isEqualToString:@"-"] && self.showLevelLine) { + CGAffineTransform transform =CGAffineTransformMakeRotation(M_PI); + [bar setTransform:transform]; + originalFrame.origin.y = bar.frame.origin.y + bar.frame.size.height; + bar.frame = originalFrame; + bar.isNegative = YES; + + } + index += 1; + } +} + +- (void)strokeChart +{ + //Add Labels + + [self viewCleanupForCollection:_bars]; + + + //Update Bar + + [self updateBar]; + + //Add chart border lines + + if (_showChartBorder) { + _chartBottomLine = [CAShapeLayer layer]; + _chartBottomLine.lineCap = kCALineCapButt; + _chartBottomLine.fillColor = [[UIColor whiteColor] CGColor]; + _chartBottomLine.lineWidth = 1.0; + _chartBottomLine.strokeEnd = 0.0; + + UIBezierPath *progressline = [UIBezierPath bezierPath]; + + [progressline moveToPoint:CGPointMake(_chartMarginLeft, self.frame.size.height - kXLabelHeight - _chartMarginBottom + _chartMarginTop)]; + [progressline addLineToPoint:CGPointMake(self.frame.size.width - _chartMarginRight, self.frame.size.height - kXLabelHeight - _chartMarginBottom + _chartMarginTop)]; + + [progressline setLineWidth:1.0]; + [progressline setLineCapStyle:kCGLineCapSquare]; + _chartBottomLine.path = progressline.CGPath; + _chartBottomLine.strokeColor = [_chartBorderColor CGColor];; + _chartBottomLine.strokeEnd = 1.0; + + [self.layer addSublayer:_chartBottomLine]; + + //Add left Chart Line + + _chartLeftLine = [CAShapeLayer layer]; + _chartLeftLine.lineCap = kCALineCapButt; + _chartLeftLine.fillColor = [[UIColor whiteColor] CGColor]; + _chartLeftLine.lineWidth = 1.0; + _chartLeftLine.strokeEnd = 0.0; + + UIBezierPath *progressLeftline = [UIBezierPath bezierPath]; + + [progressLeftline moveToPoint:CGPointMake(_chartMarginLeft, self.frame.size.height - kXLabelHeight - _chartMarginBottom + _chartMarginTop)]; + [progressLeftline addLineToPoint:CGPointMake(_chartMarginLeft, _chartMarginTop)]; + + [progressLeftline setLineWidth:1.0]; + [progressLeftline setLineCapStyle:kCGLineCapSquare]; + _chartLeftLine.path = progressLeftline.CGPath; + _chartLeftLine.strokeColor = [_chartBorderColor CGColor]; + _chartLeftLine.strokeEnd = 1.0; + + [self addBorderAnimationIfNeeded]; + [self.layer addSublayer:_chartLeftLine]; + } + + // Add Level Separator Line + if (_showLevelLine) { + _chartLevelLine = [CAShapeLayer layer]; + _chartLevelLine.lineCap = kCALineCapButt; + _chartLevelLine.fillColor = [[UIColor whiteColor] CGColor]; + _chartLevelLine.lineWidth = 1.0; + _chartLevelLine.strokeEnd = 0.0; + + UIBezierPath *progressline = [UIBezierPath bezierPath]; + + [progressline moveToPoint:CGPointMake(_chartMarginLeft, (self.frame.size.height - kXLabelHeight )/2.0)]; + [progressline addLineToPoint:CGPointMake(self.frame.size.width - _chartMarginLeft - _chartMarginRight, (self.frame.size.height - kXLabelHeight )/2.0)]; + + [progressline setLineWidth:1.0]; + [progressline setLineCapStyle:kCGLineCapSquare]; + _chartLevelLine.path = progressline.CGPath; + + _chartLevelLine.strokeColor = PNLightGrey.CGColor; + + [self addSeparatorAnimationIfNeeded]; + _chartLevelLine.strokeEnd = 1.0; + + [self.layer addSublayer:_chartLevelLine]; + } else { + if (_chartLevelLine) { + [_chartLevelLine removeFromSuperlayer]; + _chartLevelLine = nil; + } + } +} + +- (void)addBorderAnimationIfNeeded +{ + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathAnimation.duration = 0.5; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @0.0f; + pathAnimation.toValue = @1.0f; + [_chartBottomLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + + CABasicAnimation *pathLeftAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathLeftAnimation.duration = 0.5; + pathLeftAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathLeftAnimation.fromValue = @0.0f; + pathLeftAnimation.toValue = @1.0f; + [_chartLeftLine addAnimation:pathLeftAnimation forKey:@"strokeEndAnimation"]; + } +} + +- (void)addSeparatorAnimationIfNeeded +{ + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathAnimation.duration = 0.5; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @0.0f; + pathAnimation.toValue = @1.0f; + [_chartLevelLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + } +} + +- (void)viewCleanupForCollection:(NSMutableArray *)array +{ + if (array.count) { + [array makeObjectsPerformSelector:@selector(removeFromSuperview)]; + [array removeAllObjects]; + } +} + + +#pragma mark - Class extension methods + +- (UIColor *)barColorAtIndex:(NSUInteger)index +{ + if ([self.strokeColors count] == [self.yValues count]) { + return self.strokeColors[index]; + } + else { + return self.strokeColor; + } +} + +#pragma mark - Touch detection + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [self touchPoint:touches withEvent:event]; + [super touchesBegan:touches withEvent:event]; +} + +- (void)touchPoint:(NSSet *)touches withEvent:(UIEvent *)event +{ + //Get the point user touched + UITouch *touch = [touches anyObject]; + CGPoint touchPoint = [touch locationInView:self]; + UIView *subview = [self hitTest:touchPoint withEvent:nil]; + + if ([subview isKindOfClass:[PNBar class]] && [self.delegate respondsToSelector:@selector(userClickedOnBarAtIndex:)]) { + [self.delegate userClickedOnBarAtIndex:subview.tag]; + } +} + + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNChart.h b/PNChartdemo/PNChartdemo/PNChart/PNChart.h new file mode 100755 index 0000000..0835d39 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNChart.h @@ -0,0 +1,22 @@ +// +// PNChart.h +// Version 0.1 +// PNChart +// +// Created by kevin on 10/3/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "PNChart.h" +#import "PNColor.h" +#import "PNLineChart.h" +#import "PNLineChartData.h" +#import "PNLineChartDataItem.h" +#import "PNBarChart.h" +#import "PNCircleChart.h" +#import "PNChartDelegate.h" +#import "PNPieChart.h" +#import "PNScatterChart.h" +#import "PNRadarChart.h" +#import "PNRadarChartDataItem.h" diff --git a/PNChartdemo/PNChartdemo/PNChart/PNChartDelegate.h b/PNChartdemo/PNChartdemo/PNChart/PNChartDelegate.h new file mode 100755 index 0000000..6d49f7c --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNChartDelegate.h @@ -0,0 +1,33 @@ +// +// PNChartDelegate.h +// PNChartDemo +// +// Created by kevinzhow on 13-12-11. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> + +@protocol PNChartDelegate <NSObject> +@optional +/** + * Callback method that gets invoked when the user taps on the chart line. + */ +- (void)userClickedOnLinePoint:(CGPoint)point lineIndex:(NSInteger)lineIndex; + +/** + * Callback method that gets invoked when the user taps on a chart line key point. + */ +- (void)userClickedOnLineKeyPoint:(CGPoint)point + lineIndex:(NSInteger)lineIndex + pointIndex:(NSInteger)pointIndex; + +/** + * Callback method that gets invoked when the user taps on a chart bar. + */ +- (void)userClickedOnBarAtIndex:(NSInteger)barIndex; + + +- (void)userClickedOnPieIndexItem:(NSInteger)pieIndex; +- (void)didUnselectPieItem; +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNChartLabel.h b/PNChartdemo/PNChartdemo/PNChart/PNChartLabel.h new file mode 100755 index 0000000..9ba6afa --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNChartLabel.h @@ -0,0 +1,13 @@ +// +// PNChartLabel.h +// PNChart +// +// Created by kevin on 10/3/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> + +@interface PNChartLabel : UILabel + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNChartLabel.m b/PNChartdemo/PNChartdemo/PNChart/PNChartLabel.m new file mode 100755 index 0000000..b0980d1 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNChartLabel.m @@ -0,0 +1,32 @@ +// +// PNChartLabel.m +// PNChart +// +// Created by kevin on 10/3/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNChartLabel.h" + +@implementation PNChartLabel + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + self.font = [UIFont boldSystemFontOfSize:11.0f]; + self.backgroundColor = [UIColor clearColor]; + self.textAlignment = NSTextAlignmentCenter; + self.userInteractionEnabled = YES; + self.adjustsFontSizeToFitWidth = YES; + self.numberOfLines = 0; + /* if you want to see ... in large labels un-comment this line + self.minimumScaleFactor = 0.8; + */ + } + + return self; +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNCircleChart.h b/PNChartdemo/PNChartdemo/PNChart/PNCircleChart.h new file mode 100755 index 0000000..0c57376 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNCircleChart.h @@ -0,0 +1,74 @@ +// +// PNCircleChart.h +// PNChartDemo +// +// Created by kevinzhow on 13-11-30. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "PNColor.h" +#import <UICountingLabel/UICountingLabel.h> + +typedef NS_ENUM (NSUInteger, PNChartFormatType) { + PNChartFormatTypePercent, + PNChartFormatTypeDollar, + PNChartFormatTypeNone, + PNChartFormatTypeDecimal, + PNChartFormatTypeDecimalTwoPlaces, +}; + +#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI) + +@interface PNCircleChart : UIView + +- (void)strokeChart; +- (void)growChartByAmount:(NSNumber *)growAmount; +- (void)updateChartByCurrent:(NSNumber *)current; +- (void)updateChartByCurrent:(NSNumber *)current byTotal:(NSNumber *)total; +- (id)initWithFrame:(CGRect)frame + total:(NSNumber *)total + current:(NSNumber *)current + clockwise:(BOOL)clockwise; + +- (id)initWithFrame:(CGRect)frame + total:(NSNumber *)total + current:(NSNumber *)current + clockwise:(BOOL)clockwise + shadow:(BOOL)hasBackgroundShadow + shadowColor:(UIColor *)backgroundShadowColor; + +- (id)initWithFrame:(CGRect)frame + total:(NSNumber *)total + current:(NSNumber *)current + clockwise:(BOOL)clockwise + shadow:(BOOL)hasBackgroundShadow + shadowColor:(UIColor *)backgroundShadowColor +displayCountingLabel:(BOOL)displayCountingLabel; + +- (id)initWithFrame:(CGRect)frame + total:(NSNumber *)total + current:(NSNumber *)current + clockwise:(BOOL)clockwise + shadow:(BOOL)hasBackgroundShadow + shadowColor:(UIColor *)backgroundShadowColor +displayCountingLabel:(BOOL)displayCountingLabel + overrideLineWidth:(NSNumber *)overrideLineWidth; + +@property (strong, nonatomic) UICountingLabel *countingLabel; +@property (nonatomic) UIColor *strokeColor; +@property (nonatomic) UIColor *strokeColorGradientStart; +@property (nonatomic) NSNumber *total; +@property (nonatomic) NSNumber *current; +@property (nonatomic) NSNumber *lineWidth; +@property (nonatomic) NSTimeInterval duration; +@property (nonatomic) PNChartFormatType chartType; + +@property (nonatomic) CAShapeLayer *circle; +@property (nonatomic) CAShapeLayer *gradientMask; +@property (nonatomic) CAShapeLayer *circleBackground; + +@property (nonatomic) BOOL displayCountingLabel; +@property (nonatomic) BOOL displayAnimated; + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNCircleChart.m b/PNChartdemo/PNChartdemo/PNChart/PNCircleChart.m new file mode 100755 index 0000000..19f70e5 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNCircleChart.m @@ -0,0 +1,267 @@ +// +// PNCircleChart.m +// PNChartDemo +// +// Created by kevinzhow on 13-11-30. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNCircleChart.h" + +@interface PNCircleChart () +@end + +@implementation PNCircleChart + +- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise { + + return [self initWithFrame:frame + total:total + current:current + clockwise:clockwise + shadow:NO + shadowColor:[UIColor clearColor] + displayCountingLabel:YES + overrideLineWidth:@8.0f]; + +} + +- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise shadow:(BOOL)hasBackgroundShadow shadowColor:(UIColor *)backgroundShadowColor { + + return [self initWithFrame:frame + total:total + current:current + clockwise:clockwise + shadow:shadow + shadowColor:backgroundShadowColor + displayCountingLabel:YES + overrideLineWidth:@8.0f]; + +} + +- (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise shadow:(BOOL)hasBackgroundShadow shadowColor:(UIColor *)backgroundShadowColor displayCountingLabel:(BOOL)displayCountingLabel { + + return [self initWithFrame:frame + total:total + current:current + clockwise:clockwise + shadow:shadow + shadowColor:backgroundShadowColor + displayCountingLabel:displayCountingLabel + overrideLineWidth:@8.0f]; + +} + +- (id)initWithFrame:(CGRect)frame + total:(NSNumber *)total + current:(NSNumber *)current + clockwise:(BOOL)clockwise + shadow:(BOOL)hasBackgroundShadow + shadowColor:(UIColor *)backgroundShadowColor +displayCountingLabel:(BOOL)displayCountingLabel + overrideLineWidth:(NSNumber *)overrideLineWidth +{ + self = [super initWithFrame:frame]; + + if (self) { + _total = total; + _current = current; + _strokeColor = PNFreshGreen; + _duration = 1.0; + _chartType = PNChartFormatTypePercent; + _displayAnimated = YES; + + _displayCountingLabel = displayCountingLabel; + + CGFloat startAngle = clockwise ? -90.0f : 270.0f; + CGFloat endAngle = clockwise ? -90.01f : 270.01f; + + _lineWidth = overrideLineWidth; + + UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width/2.0f, self.frame.size.height/2.0f) + radius:(self.frame.size.height * 0.5) - ([_lineWidth floatValue]/2.0f) + startAngle:DEGREES_TO_RADIANS(startAngle) + endAngle:DEGREES_TO_RADIANS(endAngle) + clockwise:clockwise]; + + _circle = [CAShapeLayer layer]; + _circle.path = circlePath.CGPath; + _circle.lineCap = kCALineCapRound; + _circle.fillColor = [UIColor clearColor].CGColor; + _circle.lineWidth = [_lineWidth floatValue]; + _circle.zPosition = 1; + + _circleBackground = [CAShapeLayer layer]; + _circleBackground.path = circlePath.CGPath; + _circleBackground.lineCap = kCALineCapRound; + _circleBackground.fillColor = [UIColor clearColor].CGColor; + _circleBackground.lineWidth = [_lineWidth floatValue]; + _circleBackground.strokeColor = (hasBackgroundShadow ? backgroundShadowColor.CGColor : [UIColor clearColor].CGColor); + _circleBackground.strokeEnd = 1.0; + _circleBackground.zPosition = -1; + + [self.layer addSublayer:_circle]; + [self.layer addSublayer:_circleBackground]; + + _countingLabel = [[UICountingLabel alloc] initWithFrame:CGRectMake(0, 0, 100.0, 50.0)]; + [_countingLabel setTextAlignment:NSTextAlignmentCenter]; + [_countingLabel setFont:[UIFont boldSystemFontOfSize:16.0f]]; + [_countingLabel setTextColor:[UIColor grayColor]]; + [_countingLabel setBackgroundColor:[UIColor clearColor]]; + [_countingLabel setCenter:CGPointMake(self.frame.size.width/2.0f, self.frame.size.height/2.0f)]; + _countingLabel.method = UILabelCountingMethodEaseInOut; + if (_displayCountingLabel) { + [self addSubview:_countingLabel]; + } + } + + return self; +} + + +- (void)strokeChart +{ + // Add counting label + + if (_displayCountingLabel) { + NSString *format; + switch (self.chartType) { + case PNChartFormatTypePercent: + format = @"%d%%"; + break; + case PNChartFormatTypeDollar: + format = @"$%d"; + break; + case PNChartFormatTypeDecimal: + format = @"%.1f"; + break; + case PNChartFormatTypeDecimalTwoPlaces: + format = @"%.2f"; + break; + case PNChartFormatTypeNone: + default: + format = @"%d"; + break; + } + self.countingLabel.format = format; + [self addSubview:self.countingLabel]; + } + + + // Add circle params + + _circle.lineWidth = [_lineWidth floatValue]; + _circleBackground.lineWidth = [_lineWidth floatValue]; + _circleBackground.strokeEnd = 1.0; + _circle.strokeColor = _strokeColor.CGColor; + _circle.strokeEnd = [_current floatValue] / [_total floatValue]; + + // Check if user wants to add a gradient from the start color to the bar color + if (_strokeColorGradientStart) { + + // Add gradient + self.gradientMask = [CAShapeLayer layer]; + self.gradientMask.fillColor = [[UIColor clearColor] CGColor]; + self.gradientMask.strokeColor = [[UIColor blackColor] CGColor]; + self.gradientMask.lineWidth = _circle.lineWidth; + self.gradientMask.lineCap = kCALineCapRound; + CGRect gradientFrame = CGRectMake(0, 0, 2*self.bounds.size.width, 2*self.bounds.size.height); + self.gradientMask.frame = gradientFrame; + self.gradientMask.path = _circle.path; + + CAGradientLayer *gradientLayer = [CAGradientLayer layer]; + gradientLayer.startPoint = CGPointMake(0.5,1.0); + gradientLayer.endPoint = CGPointMake(0.5,0.0); + gradientLayer.frame = gradientFrame; + UIColor *endColor = (_strokeColor ? _strokeColor : [UIColor greenColor]); + NSArray *colors = @[ + (id)endColor.CGColor, + (id)_strokeColorGradientStart.CGColor + ]; + gradientLayer.colors = colors; + + [gradientLayer setMask:self.gradientMask]; + + [_circle addSublayer:gradientLayer]; + + self.gradientMask.strokeEnd = [_current floatValue] / [_total floatValue]; + } + + [self addAnimationIfNeeded]; +} + + + +- (void)growChartByAmount:(NSNumber *)growAmount +{ + NSNumber *updatedValue = [NSNumber numberWithFloat:[_current floatValue] + [growAmount floatValue]]; + + // Add animation + [self updateChartByCurrent:updatedValue]; +} + + +-(void)updateChartByCurrent:(NSNumber *)current{ + + [self updateChartByCurrent:current + byTotal:_total]; + +} + +-(void)updateChartByCurrent:(NSNumber *)current byTotal:(NSNumber *)total { + double totalPercentageValue = [current floatValue]/([total floatValue]/100.0); + + if (_strokeColorGradientStart) { + self.gradientMask.strokeEnd = _circle.strokeEnd; + } + + // Add animation + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathAnimation.duration = self.duration; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @([_current floatValue] / [_total floatValue]); + pathAnimation.toValue = @([current floatValue] / [total floatValue]); + + if (_strokeColorGradientStart) { + [self.gradientMask addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + } + [_circle addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + + if (_displayCountingLabel) { + [self.countingLabel countFrom:fmin([_current floatValue], [_total floatValue]) to:totalPercentageValue withDuration:self.duration]; + } + + } + else if (_displayCountingLabel) { + [self.countingLabel countFrom:totalPercentageValue to:totalPercentageValue withDuration:self.duration]; + } + + _circle.strokeEnd = [current floatValue] / [total floatValue]; + _current = current; + _total = total; +} + +- (void)addAnimationIfNeeded +{ + double percentageValue = [_current floatValue]/([_total floatValue]/100.0); + + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + pathAnimation.duration = self.duration; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @(0.0f); + pathAnimation.toValue = @([_current floatValue] / [_total floatValue]); + [_circle addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + [_countingLabel countFrom:0 to:percentageValue withDuration:self.duration]; + + if (self.gradientMask && _strokeColorGradientStart) { + [self.gradientMask addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; + } + } + else { + [_countingLabel countFrom:percentageValue to:percentageValue withDuration:self.duration]; + } +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNColor.h b/PNChartdemo/PNChartdemo/PNChart/PNColor.h new file mode 100755 index 0000000..cfd3ff8 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNColor.h @@ -0,0 +1,53 @@ +// +// PNColor.h +// PNChart +// +// Created by kevin on 13-6-8. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +/* + * System Versioning Preprocessor Macros + */ + +#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width) + +#define PNGrey [UIColor colorWithRed:246.0 / 255.0 green:246.0 / 255.0 blue:246.0 / 255.0 alpha:1.0f] +#define PNLightBlue [UIColor colorWithRed:94.0 / 255.0 green:147.0 / 255.0 blue:196.0 / 255.0 alpha:1.0f] +#define PNGreen [UIColor colorWithRed:77.0 / 255.0 green:186.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] +#define PNTitleColor [UIColor colorWithRed:0.0 / 255.0 green:189.0 / 255.0 blue:113.0 / 255.0 alpha:1.0f] +#define PNButtonGrey [UIColor colorWithRed:141.0 / 255.0 green:141.0 / 255.0 blue:141.0 / 255.0 alpha:1.0f] +#define PNLightGreen [UIColor colorWithRed:77.0 / 255.0 green:216.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] +#define PNFreshGreen [UIColor colorWithRed:77.0 / 255.0 green:196.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] +#define PNDeepGreen [UIColor colorWithRed:77.0 / 255.0 green:176.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] +#define PNRed [UIColor colorWithRed:245.0 / 255.0 green:94.0 / 255.0 blue:78.0 / 255.0 alpha:1.0f] +#define PNMauve [UIColor colorWithRed:88.0 / 255.0 green:75.0 / 255.0 blue:103.0 / 255.0 alpha:1.0f] +#define PNBrown [UIColor colorWithRed:119.0 / 255.0 green:107.0 / 255.0 blue:95.0 / 255.0 alpha:1.0f] +#define PNBlue [UIColor colorWithRed:82.0 / 255.0 green:116.0 / 255.0 blue:188.0 / 255.0 alpha:1.0f] +#define PNDarkBlue [UIColor colorWithRed:121.0 / 255.0 green:134.0 / 255.0 blue:142.0 / 255.0 alpha:1.0f] +#define PNYellow [UIColor colorWithRed:242.0 / 255.0 green:197.0 / 255.0 blue:117.0 / 255.0 alpha:1.0f] +#define PNWhite [UIColor colorWithRed:255.0 / 255.0 green:255.0 / 255.0 blue:255.0 / 255.0 alpha:1.0f] +#define PNDeepGrey [UIColor colorWithRed:99.0 / 255.0 green:99.0 / 255.0 blue:99.0 / 255.0 alpha:1.0f] +#define PNPinkGrey [UIColor colorWithRed:200.0 / 255.0 green:193.0 / 255.0 blue:193.0 / 255.0 alpha:1.0f] +#define PNHealYellow [UIColor colorWithRed:245.0 / 255.0 green:242.0 / 255.0 blue:238.0 / 255.0 alpha:1.0f] +#define PNLightGrey [UIColor colorWithRed:225.0 / 255.0 green:225.0 / 255.0 blue:225.0 / 255.0 alpha:1.0f] +#define PNCleanGrey [UIColor colorWithRed:251.0 / 255.0 green:251.0 / 255.0 blue:251.0 / 255.0 alpha:1.0f] +#define PNLightYellow [UIColor colorWithRed:241.0 / 255.0 green:240.0 / 255.0 blue:240.0 / 255.0 alpha:1.0f] +#define PNDarkYellow [UIColor colorWithRed:152.0 / 255.0 green:150.0 / 255.0 blue:159.0 / 255.0 alpha:1.0f] +#define PNPinkDark [UIColor colorWithRed:170.0 / 255.0 green:165.0 / 255.0 blue:165.0 / 255.0 alpha:1.0f] +#define PNCloudWhite [UIColor colorWithRed:244.0 / 255.0 green:244.0 / 255.0 blue:244.0 / 255.0 alpha:1.0f] +#define PNBlack [UIColor colorWithRed:45.0 / 255.0 green:45.0 / 255.0 blue:45.0 / 255.0 alpha:1.0f] +#define PNStarYellow [UIColor colorWithRed:252.0 / 255.0 green:223.0 / 255.0 blue:101.0 / 255.0 alpha:1.0f] +#define PNTwitterColor [UIColor colorWithRed:0.0 / 255.0 green:171.0 / 255.0 blue:243.0 / 255.0 alpha:1.0] +#define PNWeiboColor [UIColor colorWithRed:250.0 / 255.0 green:0.0 / 255.0 blue:33.0 / 255.0 alpha:1.0] +#define PNiOSGreenColor [UIColor colorWithRed:98.0 / 255.0 green:247.0 / 255.0 blue:77.0 / 255.0 alpha:1.0] + + +@interface PNColor : NSObject + +- (UIImage *)imageFromColor:(UIColor *)color; + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNColor.m b/PNChartdemo/PNChartdemo/PNChart/PNColor.m new file mode 100755 index 0000000..2ebc8c0 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNColor.m @@ -0,0 +1,29 @@ +// +// PNColor.m +// PNChart +// +// Created by kevin on 13-6-8. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNColor.h" +#import <UIKit/UIKit.h> + +@implementation PNColor + +- (UIImage *)imageFromColor:(UIColor *)color +{ + CGRect rect = CGRectMake(0, 0, 1, 1); + + UIGraphicsBeginImageContext(rect.size); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetFillColorWithColor(context, [color CGColor]); + CGContextFillRect(context, rect); + UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return img; +} + + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNGenericChart.h b/PNChartdemo/PNChartdemo/PNChart/PNGenericChart.h new file mode 100755 index 0000000..829d84a --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNGenericChart.h @@ -0,0 +1,48 @@ +// +// PNGenericChart.h +// PNChartDemo +// +// Created by Andi Palo on 26/02/15. +// Copyright (c) 2015 kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> + +typedef NS_ENUM(NSUInteger, PNLegendPosition) { + PNLegendPositionTop = 0, + PNLegendPositionBottom = 1, + PNLegendPositionLeft = 2, + PNLegendPositionRight = 3 +}; + +typedef NS_ENUM(NSUInteger, PNLegendItemStyle) { + PNLegendItemStyleStacked = 0, + PNLegendItemStyleSerial = 1 +}; + +@interface PNGenericChart : UIView + +@property (assign, nonatomic) BOOL hasLegend; +@property (assign, nonatomic) PNLegendPosition legendPosition; +@property (assign, nonatomic) PNLegendItemStyle legendStyle; + +@property (assign, nonatomic) UIFont *legendFont; +@property (assign, nonatomic) UIColor *legendFontColor; +@property (assign, nonatomic) NSUInteger labelRowsInSerialMode; + +/** Display the chart with or without animation. Default is YES. **/ +@property (nonatomic) BOOL displayAnimated; + +/** + * returns the Legend View, or nil if no chart data is present. + * The origin of the legend frame is 0,0 but you can set it with setFrame:(CGRect) + * + * @param mWidth Maximum width of legend. Height will depend on this and font size + * + * @return UIView of Legend + */ +- (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth; + + +- (void) setupDefaultValues; +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNGenericChart.m b/PNChartdemo/PNChartdemo/PNChart/PNGenericChart.m new file mode 100755 index 0000000..c54ac37 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNGenericChart.m @@ -0,0 +1,54 @@ +// +// PNGenericChart.m +// PNChartDemo +// +// Created by Andi Palo on 26/02/15. +// Copyright (c) 2015 kevinzhow. All rights reserved. +// + +#import "PNGenericChart.h" + +@interface PNGenericChart () + + + +@end + +@implementation PNGenericChart + +/* +// Only override drawRect: if you perform custom drawing. +// An empty implementation adversely affects performance during animation. +- (void)drawRect:(CGRect)rect { + // Drawing code +} +*/ + +- (void) setupDefaultValues{ + self.hasLegend = YES; + self.legendPosition = PNLegendPositionBottom; + self.legendStyle = PNLegendItemStyleStacked; + self.labelRowsInSerialMode = 1; + self.displayAnimated = YES; +} + + + +/** + * to be implemented in subclass + */ +- (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth{ + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (void) setLabelRowsInSerialMode:(NSUInteger)num{ + if (self.legendStyle == PNLegendItemStyleSerial) { + _labelRowsInSerialMode = num; + }else{ + _labelRowsInSerialMode = 1; + } +} + + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNLineChart.h b/PNChartdemo/PNChartdemo/PNChart/PNLineChart.h new file mode 100755 index 0000000..b30878a --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNLineChart.h @@ -0,0 +1,110 @@ +// +// PNLineChart.h +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import <QuartzCore/QuartzCore.h> +#import "PNChartDelegate.h" +#import "PNGenericChart.h" + +@interface PNLineChart : PNGenericChart + +/** + * Draws the chart in an animated fashion. + */ +- (void)strokeChart; + +@property (nonatomic, weak) id<PNChartDelegate> delegate; + +@property (nonatomic) NSArray *xLabels; +@property (nonatomic) NSArray *yLabels; + +/** + * Array of `LineChartData` objects, one for each line. + */ +@property (nonatomic) NSArray *chartData; + +@property (nonatomic) NSMutableArray *pathPoints; +@property (nonatomic) NSMutableArray *xChartLabels; +@property (nonatomic) NSMutableArray *yChartLabels; + +@property (nonatomic) CGFloat xLabelWidth; +@property (nonatomic) UIFont *xLabelFont; +@property (nonatomic) UIColor *xLabelColor; +@property (nonatomic) CGFloat yValueMax; +@property (nonatomic) CGFloat yFixedValueMax; +@property (nonatomic) CGFloat yFixedValueMin; +@property (nonatomic) CGFloat yValueMin; +@property (nonatomic) NSInteger yLabelNum; +@property (nonatomic) CGFloat yLabelHeight; +@property (nonatomic) UIFont *yLabelFont; +@property (nonatomic) UIColor *yLabelColor; +@property (nonatomic) CGFloat chartCavanHeight; +@property (nonatomic) CGFloat chartCavanWidth; +@property (nonatomic) BOOL showLabel; +@property (nonatomic) BOOL showGenYLabels; +@property (nonatomic) BOOL showYGridLines; +@property (nonatomic) UIColor *yGridLinesColor; +@property (nonatomic) BOOL thousandsSeparator; + +@property (nonatomic) CGFloat chartMarginLeft; +@property (nonatomic) CGFloat chartMarginRight; +@property (nonatomic) CGFloat chartMarginTop; +@property (nonatomic) CGFloat chartMarginBottom; + +/** + * Controls whether to show the coordinate axis. Default is NO. + */ +@property (nonatomic, getter = isShowCoordinateAxis) BOOL showCoordinateAxis; +@property (nonatomic) UIColor *axisColor; +@property (nonatomic) CGFloat axisWidth; + +@property (nonatomic, strong) NSString *xUnit; +@property (nonatomic, strong) NSString *yUnit; + +/** + * String formatter for float values in y-axis labels. If not set, defaults to @"%1.f" + */ +@property (nonatomic, strong) NSString *yLabelFormat; + +/** + * Block formatter for custom string in y-axis labels. If not set, defaults to yLabelFormat + */ +@property (nonatomic, copy) NSString* (^yLabelBlockFormatter)(CGFloat); + + +/** + * Controls whether to curve the line chart or not + */ +@property (nonatomic) BOOL showSmoothLines; + +- (void)setXLabels:(NSArray *)xLabels withWidth:(CGFloat)width; + +/** + * Update Chart Value + */ + +- (void)updateChartData:(NSArray *)data; + + +/** + * returns the Legend View, or nil if no chart data is present. + * The origin of the legend frame is 0,0 but you can set it with setFrame:(CGRect) + * + * @param mWidth Maximum width of legend. Height will depend on this and font size + * + * @return UIView of Legend + */ +- (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth; + + ++ (CGSize)sizeOfString:(NSString *)text withWidth:(float)width font:(UIFont *)font; + ++ (CGPoint)midPointBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2; ++ (CGPoint)controlPointBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2; + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNLineChart.m b/PNChartdemo/PNChartdemo/PNChart/PNLineChart.m new file mode 100755 index 0000000..d03e494 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNLineChart.m @@ -0,0 +1,1284 @@ +// +// PNLineChart.m +// PNChartDemo +// +// Created by kevin on 11/7/13. +// Copyright (c) 2013��� kevinzhow. All rights reserved. +// + +#import "PNLineChart.h" +#import "PNColor.h" +#import "PNChartLabel.h" +#import "PNLineChartData.h" +#import "PNLineChartDataItem.h" + +@interface PNLineChart () + +@property(nonatomic) NSMutableArray *chartLineArray; // Array[CAShapeLayer] +@property(nonatomic) NSMutableArray *chartPointArray; // Array[CAShapeLayer] save the point layer + +@property(nonatomic) NSMutableArray *chartPath; // Array of line path, one for each line. +@property(nonatomic) NSMutableArray *pointPath; // Array of point path, one for each line +@property(nonatomic) NSMutableArray *endPointsOfPath; // Array of start and end points of each line path, one for each line + +@property(nonatomic) CABasicAnimation *pathAnimation; // will be set to nil if _displayAnimation is NO + +// display grade +@property(nonatomic) NSMutableArray *gradeStringPaths; +@property(nonatomic) NSMutableArray *progressLinePathsColors; //Array of colors when drawing each line.if chartData.rangeColors is set then different colors will be + +@end + +@implementation PNLineChart + +@synthesize pathAnimation = _pathAnimation; + +#pragma mark initialization + +- (id)initWithCoder:(NSCoder *)coder { + self = [super initWithCoder:coder]; + + if (self) { + [self setupDefaultValues]; + } + + return self; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + + if (self) { + [self setupDefaultValues]; + } + + return self; +} + + +#pragma mark instance methods + +- (void)setYLabels { + CGFloat yStep = (_yValueMax - _yValueMin) / _yLabelNum; + CGFloat yStepHeight = _chartCavanHeight / _yLabelNum; + + if (_yChartLabels) { + for (PNChartLabel *label in _yChartLabels) { + [label removeFromSuperview]; + } + } else { + _yChartLabels = [NSMutableArray new]; + } + + if (yStep == 0.0) { + PNChartLabel *minLabel = [[PNChartLabel alloc] initWithFrame:CGRectMake(0.0, (NSInteger) _chartCavanHeight, (NSInteger) _chartMarginBottom, (NSInteger) _yLabelHeight)]; + minLabel.text = [self formatYLabel:0.0]; + [self setCustomStyleForYLabel:minLabel]; + [self addSubview:minLabel]; + [_yChartLabels addObject:minLabel]; + + PNChartLabel *midLabel = [[PNChartLabel alloc] initWithFrame:CGRectMake(0.0, (NSInteger) (_chartCavanHeight / 2), (NSInteger) _chartMarginBottom, (NSInteger) _yLabelHeight)]; + midLabel.text = [self formatYLabel:_yValueMax]; + [self setCustomStyleForYLabel:midLabel]; + [self addSubview:midLabel]; + [_yChartLabels addObject:midLabel]; + + PNChartLabel *maxLabel = [[PNChartLabel alloc] initWithFrame:CGRectMake(0.0, 0.0, (NSInteger) _chartMarginBottom, (NSInteger) _yLabelHeight)]; + maxLabel.text = [self formatYLabel:_yValueMax * 2]; + [self setCustomStyleForYLabel:maxLabel]; + [self addSubview:maxLabel]; + [_yChartLabels addObject:maxLabel]; + + } else { + NSInteger index = 0; + NSInteger num = _yLabelNum + 1; + + while (num > 0) { + CGRect labelFrame = CGRectMake(0.0, + (NSInteger) (_chartCavanHeight + _chartMarginTop - index * yStepHeight), + (CGFloat) ((NSInteger) _chartMarginLeft * 0.9), + (NSInteger) _yLabelHeight); + PNChartLabel *label = [[PNChartLabel alloc] initWithFrame:labelFrame]; + [label setTextAlignment:NSTextAlignmentRight]; + label.text = [self formatYLabel:_yValueMin + (yStep * index)]; + [self setCustomStyleForYLabel:label]; + [self addSubview:label]; + [_yChartLabels addObject:label]; + index += 1; + num -= 1; + } + } +} + +- (void)setYLabels:(NSArray *)yLabels { + _showGenYLabels = NO; + _yLabelNum = yLabels.count - 1; + + CGFloat yLabelHeight; + if (_showLabel) { + yLabelHeight = _chartCavanHeight / [yLabels count]; + } else { + yLabelHeight = (self.frame.size.height) / [yLabels count]; + } + + return [self setYLabels:yLabels withHeight:yLabelHeight]; +} + +- (void)setYLabels:(NSArray *)yLabels withHeight:(CGFloat)height { + _yLabels = yLabels; + _yLabelHeight = height; + if (_yChartLabels) { + for (PNChartLabel *label in _yChartLabels) { + [label removeFromSuperview]; + } + } else { + _yChartLabels = [NSMutableArray new]; + } + + NSString *labelText; + + if (_showLabel) { + CGFloat yStepHeight = _chartCavanHeight / _yLabelNum; + + for (int index = 0; index < yLabels.count; index++) { + labelText = yLabels[(NSUInteger) index]; + + NSInteger y = (NSInteger) (_chartCavanHeight + _chartMarginTop - index * yStepHeight); + + PNChartLabel *label = [[PNChartLabel alloc] initWithFrame:CGRectMake(0.0, y, (CGFloat) ((NSInteger) _chartMarginLeft * 0.9), (NSInteger) _yLabelHeight)]; + [label setTextAlignment:NSTextAlignmentRight]; + label.text = labelText; + [self setCustomStyleForYLabel:label]; + [self addSubview:label]; + [_yChartLabels addObject:label]; + } + } +} + +- (CGFloat)computeEqualWidthForXLabels:(NSArray *)xLabels { + CGFloat xLabelWidth; + + if (_showLabel) { + xLabelWidth = _chartCavanWidth / [xLabels count]; + } else { + xLabelWidth = (self.frame.size.width) / [xLabels count]; + } + + return xLabelWidth; +} + + +- (void)setXLabels:(NSArray *)xLabels { + CGFloat xLabelWidth; + + if (_showLabel) { + xLabelWidth = _chartCavanWidth / [xLabels count]; + } else { + xLabelWidth = (self.frame.size.width - _chartMarginLeft - _chartMarginRight) / [xLabels count]; + } + + return [self setXLabels:xLabels withWidth:xLabelWidth]; +} + +- (void)setXLabels:(NSArray *)xLabels withWidth:(CGFloat)width { + _xLabels = xLabels; + _xLabelWidth = width; + if (_xChartLabels) { + for (PNChartLabel *label in _xChartLabels) { + [label removeFromSuperview]; + } + } else { + _xChartLabels = [NSMutableArray new]; + } + + NSString *labelText; + + if (_showLabel) { + for (NSUInteger index = 0; index < xLabels.count; index++) { + labelText = xLabels[index]; + + NSInteger x = (NSInteger) (index * _xLabelWidth + _chartMarginLeft); + NSInteger y = (NSInteger) (_chartMarginBottom + _chartCavanHeight); + + PNChartLabel *label = [[PNChartLabel alloc] initWithFrame:CGRectMake(x, y, (NSInteger) _xLabelWidth, (NSInteger) _chartMarginBottom)]; + [label setTextAlignment:NSTextAlignmentCenter]; + label.text = labelText; + [self setCustomStyleForXLabel:label]; + [self addSubview:label]; + [_xChartLabels addObject:label]; + } + } +} + +- (void)setCustomStyleForXLabel:(UILabel *)label { + if (_xLabelFont) { + label.font = _xLabelFont; + } + + if (_xLabelColor) { + label.textColor = _xLabelColor; + } + +} + +- (void)setCustomStyleForYLabel:(UILabel *)label { + if (_yLabelFont) { + label.font = _yLabelFont; + } + + if (_yLabelColor) { + label.textColor = _yLabelColor; + } +} + +#pragma mark - Touch at point + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [self touchPoint:touches withEvent:event]; + [self touchKeyPoint:touches withEvent:event]; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + [self touchPoint:touches withEvent:event]; + [self touchKeyPoint:touches withEvent:event]; +} + +- (void)touchPoint:(NSSet *)touches withEvent:(UIEvent *)event { + // Get the point user touched + UITouch *touch = [touches anyObject]; + CGPoint touchPoint = [touch locationInView:self]; + + for (NSUInteger p = 0; p < _pathPoints.count; p++) { + NSArray *linePointsArray = _endPointsOfPath[p]; + + for (NSUInteger i = 0; i < (int) linePointsArray.count - 1; i += 2) { + CGPoint p1 = [linePointsArray[i] CGPointValue]; + CGPoint p2 = [linePointsArray[i + 1] CGPointValue]; + + // Closest distance from point to line + float distance = (float) fabs(((p2.x - p1.x) * (touchPoint.y - p1.y)) - ((p1.x - touchPoint.x) * (p1.y - p2.y))); + distance /= hypot(p2.x - p1.x, p1.y - p2.y); + + if (distance <= 5.0) { + // Conform to delegate parameters, figure out what bezier path this CGPoint belongs to. + NSUInteger lineIndex = 0; + for (NSArray<UIBezierPath *> *paths in _chartPath) { + for (UIBezierPath *path in paths) { + BOOL pointContainsPath = CGPathContainsPoint(path.CGPath, NULL, p1, NO); + if (pointContainsPath) { + [_delegate userClickedOnLinePoint:touchPoint lineIndex:lineIndex]; + return; + } + } + lineIndex++; + } + } + } + } +} + +- (void)touchKeyPoint:(NSSet *)touches withEvent:(UIEvent *)event { + // Get the point user touched + UITouch *touch = [touches anyObject]; + CGPoint touchPoint = [touch locationInView:self]; + + for (NSUInteger p = 0; p < _pathPoints.count; p++) { + NSArray *linePointsArray = _pathPoints[p]; + + for (NSUInteger i = 0; i < (int) linePointsArray.count - 1; i += 1) { + CGPoint p1 = [linePointsArray[i] CGPointValue]; + CGPoint p2 = [linePointsArray[i + 1] CGPointValue]; + + float distanceToP1 = (float) fabs(hypot(touchPoint.x - p1.x, touchPoint.y - p1.y)); + float distanceToP2 = (float) hypot(touchPoint.x - p2.x, touchPoint.y - p2.y); + + float distance = MIN(distanceToP1, distanceToP2); + + if (distance <= 10.0) { + [_delegate userClickedOnLineKeyPoint:touchPoint + lineIndex:p + pointIndex:(distance == distanceToP2 ? i + 1 : i)]; + + return; + } + } + } +} + +#pragma mark - Draw Chart + +- (void)populateChartLines { + for (NSUInteger lineIndex = 0; lineIndex < self.chartData.count; lineIndex++) { + NSArray<UIBezierPath *> *progressLines = self.chartPath[lineIndex]; + // each chart line can be divided into multiple paths because + // we need ot draw each path with different color + // if there is not rangeColors then there is only one progressLinePath per chart + NSArray<UIColor *> *progressLineColors = self.progressLinePathsColors[lineIndex]; + [self.chartLineArray[lineIndex] removeAllObjects]; + NSUInteger progressLineIndex = 0;; + for (UIBezierPath *progressLinePath in progressLines) { + PNLineChartData *chartData = self.chartData[lineIndex]; + CAShapeLayer *chartLine = [CAShapeLayer layer]; + chartLine.lineCap = kCALineCapButt; + chartLine.lineJoin = kCALineJoinMiter; + chartLine.fillColor = self.backgroundColor.CGColor; + chartLine.lineWidth = chartData.lineWidth; + chartLine.path = progressLinePath.CGPath; + chartLine.strokeEnd = 0.0; + chartLine.strokeColor = progressLineColors[progressLineIndex].CGColor; + [self.layer addSublayer:chartLine]; + [self.chartLineArray[lineIndex] addObject:chartLine]; + progressLineIndex++; + } + } +} + +/* + * strokeChart should remove the previously drawn chart lines and points + * and then proceed to draw the new lines + */ +- (void)strokeChart { + [self removeLayers]; + // remove all shape layers before adding new ones + [self recreatePointLayers]; + // Cavan height and width needs to be set before + // setNeedsDisplay is invoked because setNeedsDisplay + // will invoke drawRect and if Cavan dimensions is not + // set the chart will be misplaced + [self resetCavanHeight]; + [self prepareYLabelsWithData:_chartData]; + + _chartPath = [[NSMutableArray alloc] init]; + _pointPath = [[NSMutableArray alloc] init]; + _gradeStringPaths = [NSMutableArray array]; + _progressLinePathsColors = [[NSMutableArray alloc] init]; + + [self calculateChartPath:_chartPath + andPointsPath:_pointPath + andPathKeyPoints:_pathPoints + andPathStartEndPoints:_endPointsOfPath + andProgressLinePathsColors:_progressLinePathsColors]; + [self populateChartLines]; + // Draw each line + for (NSUInteger lineIndex = 0; lineIndex < self.chartData.count; lineIndex++) { + PNLineChartData *chartData = self.chartData[lineIndex]; + NSArray<CAShapeLayer *> *chartLines =self.chartLineArray[lineIndex]; + CAShapeLayer *pointLayer = (CAShapeLayer *) self.chartPointArray[lineIndex]; + UIGraphicsBeginImageContext(self.frame.size); + if (chartData.inflexionPointColor) { + pointLayer.strokeColor = [[chartData.inflexionPointColor + colorWithAlphaComponent:chartData.alpha] CGColor]; + } else { + pointLayer.strokeColor = [PNGreen CGColor]; + } + // setup the color of the chart line + NSArray<UIBezierPath *> *progressLines = _chartPath[lineIndex]; + UIBezierPath *pointPath = _pointPath[lineIndex]; + + pointLayer.path = pointPath.CGPath; + + [CATransaction begin]; + for (NSUInteger index = 0; index < progressLines.count; index++) { + CAShapeLayer *chartLine = chartLines[index]; + //chartLine strokeColor is already set. no need to override here + [chartLine addAnimation:self.pathAnimation forKey:@"strokeEndAnimation"]; + chartLine.strokeEnd = 1.0; + } + + // if you want cancel the point animation, comment this code, the point will show immediately + if (chartData.inflexionPointStyle != PNLineChartPointStyleNone) { + [pointLayer addAnimation:self.pathAnimation forKey:@"strokeEndAnimation"]; + } + + [CATransaction commit]; + + NSMutableArray *textLayerArray = self.gradeStringPaths[lineIndex]; + for (CATextLayer *textLayer in textLayerArray) { + CABasicAnimation *fadeAnimation = [self fadeAnimation]; + [textLayer addAnimation:fadeAnimation forKey:nil]; + } + + UIGraphicsEndImageContext(); + } + [self setNeedsDisplay]; +} + + +- (void)calculateChartPath:(NSMutableArray *)chartPath + andPointsPath:(NSMutableArray *)pointsPath + andPathKeyPoints:(NSMutableArray *)pathPoints + andPathStartEndPoints:(NSMutableArray *)pointsOfPath +andProgressLinePathsColors:(NSMutableArray *)progressLinePathsColors { + + // Draw each line + + for (NSUInteger lineIndex = 0; lineIndex < self.chartData.count; lineIndex++) { + PNLineChartData *chartData = self.chartData[lineIndex]; + + CGFloat yValue; + NSMutableArray<UIBezierPath *> *progressLines = [NSMutableArray new]; + NSMutableArray<UIColor *> *progressLineColors = [NSMutableArray new]; + + UIBezierPath *pointPath = [UIBezierPath bezierPath]; + + + [chartPath insertObject:progressLines atIndex:lineIndex]; + [pointsPath insertObject:pointPath atIndex:lineIndex]; + [progressLinePathsColors insertObject:progressLineColors atIndex:lineIndex]; + + + NSMutableArray *gradePathArray = [NSMutableArray array]; + [self.gradeStringPaths addObject:gradePathArray]; + + NSMutableArray *linePointsArray = [[NSMutableArray alloc] init]; + NSMutableArray *lineStartEndPointsArray = [[NSMutableArray alloc] init]; + int last_x = 0; + int last_y = 0; + NSMutableArray<NSDictionary<NSString *, NSValue *> *> *progressLinePaths = [NSMutableArray new]; + UIColor *defaultColor = chartData.color != nil ? chartData.color : [UIColor greenColor]; + CGFloat inflexionWidth = chartData.inflexionPointWidth; + + for (NSUInteger i = 0; i < chartData.itemCount; i++) { + + NSValue *from = nil; + NSValue *to = nil; + + yValue = chartData.getData(i).y; + + int x = (int) (i * _xLabelWidth + _chartMarginLeft + _xLabelWidth / 2.0); + int y = (int)[self yValuePositionInLineChart:yValue]; + + // Circular point + if (chartData.inflexionPointStyle == PNLineChartPointStyleCircle) { + + CGRect circleRect = CGRectMake(x - inflexionWidth / 2, y - inflexionWidth / 2, inflexionWidth, inflexionWidth); + CGPoint circleCenter = CGPointMake(circleRect.origin.x + (circleRect.size.width / 2), circleRect.origin.y + (circleRect.size.height / 2)); + + [pointPath moveToPoint:CGPointMake(circleCenter.x + (inflexionWidth / 2), circleCenter.y)]; + [pointPath addArcWithCenter:circleCenter radius:inflexionWidth / 2 startAngle:0 endAngle:(CGFloat) (2 * M_PI) clockwise:YES]; + + //jet text display text + if (chartData.showPointLabel) { + [gradePathArray addObject:[self createPointLabelFor:chartData.getData(i).rawY pointCenter:circleCenter width:inflexionWidth withChartData:chartData]]; + } + + if (i > 0) { + + // calculate the point for line + float distance = (float) sqrt(pow(x - last_x, 2) + pow(y - last_y, 2)); + float last_x1 = last_x + (inflexionWidth / 2) / distance * (x - last_x); + float last_y1 = last_y + (inflexionWidth / 2) / distance * (y - last_y); + float x1 = x - (inflexionWidth / 2) / distance * (x - last_x); + float y1 = y - (inflexionWidth / 2) / distance * (y - last_y); + from = [NSValue valueWithCGPoint:CGPointMake(last_x1, last_y1)]; + to = [NSValue valueWithCGPoint:CGPointMake(x1, y1)]; + } + } + // Square point + else if (chartData.inflexionPointStyle == PNLineChartPointStyleSquare) { + + CGRect squareRect = CGRectMake(x - inflexionWidth / 2, y - inflexionWidth / 2, inflexionWidth, inflexionWidth); + CGPoint squareCenter = CGPointMake(squareRect.origin.x + (squareRect.size.width / 2), squareRect.origin.y + (squareRect.size.height / 2)); + + [pointPath moveToPoint:CGPointMake(squareCenter.x - (inflexionWidth / 2), squareCenter.y - (inflexionWidth / 2))]; + [pointPath addLineToPoint:CGPointMake(squareCenter.x + (inflexionWidth / 2), squareCenter.y - (inflexionWidth / 2))]; + [pointPath addLineToPoint:CGPointMake(squareCenter.x + (inflexionWidth / 2), squareCenter.y + (inflexionWidth / 2))]; + [pointPath addLineToPoint:CGPointMake(squareCenter.x - (inflexionWidth / 2), squareCenter.y + (inflexionWidth / 2))]; + [pointPath closePath]; + + // text display text + if (chartData.showPointLabel) { + [gradePathArray addObject:[self createPointLabelFor:chartData.getData(i).rawY pointCenter:squareCenter width:inflexionWidth withChartData:chartData]]; + } + + if (i > 0) { + + // calculate the point for line + float distance = (float) sqrt(pow(x - last_x, 2) + pow(y - last_y, 2)); + float last_x1 = last_x + (inflexionWidth / 2); + float last_y1 = last_y + (inflexionWidth / 2) / distance * (y - last_y); + float x1 = x - (inflexionWidth / 2); + float y1 = y - (inflexionWidth / 2) / distance * (y - last_y); + from = [NSValue valueWithCGPoint:CGPointMake(last_x1, last_y1)]; + to = [NSValue valueWithCGPoint:CGPointMake(x1, y1)]; + } + } + // Triangle point + else if (chartData.inflexionPointStyle == PNLineChartPointStyleTriangle) { + + CGRect squareRect = CGRectMake(x - inflexionWidth / 2, y - inflexionWidth / 2, inflexionWidth, inflexionWidth); + + CGPoint startPoint = CGPointMake(squareRect.origin.x, squareRect.origin.y + squareRect.size.height); + CGPoint endPoint = CGPointMake(squareRect.origin.x + (squareRect.size.width / 2), squareRect.origin.y); + CGPoint middlePoint = CGPointMake(squareRect.origin.x + (squareRect.size.width), squareRect.origin.y + squareRect.size.height); + + [pointPath moveToPoint:startPoint]; + [pointPath addLineToPoint:middlePoint]; + [pointPath addLineToPoint:endPoint]; + [pointPath closePath]; + + // text display text + if (chartData.showPointLabel) { + [gradePathArray addObject:[self createPointLabelFor:chartData.getData(i).rawY pointCenter:middlePoint width:inflexionWidth withChartData:chartData]]; + } + + if (i > 0) { + // calculate the point for triangle + float distance = (float) (sqrt(pow(x - last_x, 2) + pow(y - last_y, 2)) * 1.4); + float last_x1 = last_x + (inflexionWidth / 2) / distance * (x - last_x); + float last_y1 = last_y + (inflexionWidth / 2) / distance * (y - last_y); + float x1 = x - (inflexionWidth / 2) / distance * (x - last_x); + float y1 = y - (inflexionWidth / 2) / distance * (y - last_y); + from = [NSValue valueWithCGPoint:CGPointMake(last_x1, last_y1)]; + to = [NSValue valueWithCGPoint:CGPointMake(x1, y1)]; + } + } else { + + if (i > 0) { + from = [NSValue valueWithCGPoint:CGPointMake(last_x, last_y)]; + to = [NSValue valueWithCGPoint:CGPointMake(x, y)]; + } + } + if(from != nil && to != nil) { + [progressLinePaths addObject:@{@"from": from, @"to":to}]; + [lineStartEndPointsArray addObject:from]; + [lineStartEndPointsArray addObject:to]; + } + [linePointsArray addObject:[NSValue valueWithCGPoint:CGPointMake(x, y)]]; + last_x = x; + last_y = y; + } + + [pointsOfPath addObject:[lineStartEndPointsArray copy]]; + [pathPoints addObject:[linePointsArray copy]]; + // if rangeColors is not nil then it means we need to draw the chart + // with different colors. colorRangesBetweenP1.. function takes care of + // partitioning the p1->p2 into segments from which we can create UIBezierPath + if (self.showSmoothLines && chartData.itemCount >= 4) { + for (NSDictionary<NSString *, NSValue *> *item in progressLinePaths) { + NSArray<NSDictionary *> *calculatedRanges = + [self colorRangesBetweenP1:[item[@"from"] CGPointValue] + P2:[item[@"to"] CGPointValue] + rangeColors:chartData.rangeColors + defaultColor:defaultColor]; + for (NSDictionary *range in calculatedRanges) { +// NSLog(@"range : %@ range: %@ color %@", range[@"from"], range[@"to"], range[@"color"]); + UIBezierPath *currentProgressLine = [UIBezierPath bezierPath]; + CGPoint segmentP1 = [range[@"from"] CGPointValue]; + CGPoint segmentP2 = [range[@"to"] CGPointValue]; + [currentProgressLine moveToPoint:segmentP1]; + CGPoint midPoint = [PNLineChart midPointBetweenPoint1:segmentP1 andPoint2:segmentP2]; + [currentProgressLine addQuadCurveToPoint:midPoint + controlPoint:[PNLineChart controlPointBetweenPoint1:midPoint andPoint2:segmentP1]]; + [currentProgressLine addQuadCurveToPoint:segmentP2 + controlPoint:[PNLineChart controlPointBetweenPoint1:midPoint andPoint2:segmentP2]]; + [progressLines addObject:currentProgressLine]; + [progressLineColors addObject:range[@"color"]]; + } + } + } else { + for (NSDictionary<NSString *, NSValue *> *item in progressLinePaths) { + NSArray<NSDictionary *> *calculatedRanges = + [self colorRangesBetweenP1:[item[@"from"] CGPointValue] + P2:[item[@"to"] CGPointValue] + rangeColors:chartData.rangeColors + defaultColor:defaultColor]; + for (NSDictionary *range in calculatedRanges) { +// NSLog(@"range : %@ range: %@ color %@", range[@"from"], range[@"to"], range[@"color"]); + UIBezierPath *currentProgressLine = [UIBezierPath bezierPath]; + [currentProgressLine moveToPoint:[range[@"from"] CGPointValue]]; + [currentProgressLine addLineToPoint:[range[@"to"] CGPointValue]]; + [progressLines addObject:currentProgressLine]; + [progressLineColors addObject:range[@"color"]]; + } + } + } + } +} + +#pragma mark - Set Chart Data + +- (void)setChartData:(NSArray *)data { + if (data != _chartData) { + _chartData = data; + } +} + + +- (void)removeLayers { + for (NSArray<CALayer *> *layers in self.chartLineArray) { + for (CALayer *layer in layers) { + [layer removeFromSuperlayer]; + } + } + for (CALayer *layer in self.chartPointArray) { + [layer removeFromSuperlayer]; + } + self.chartLineArray = [NSMutableArray arrayWithCapacity:_chartData.count]; + self.chartPointArray = [NSMutableArray arrayWithCapacity:_chartData.count]; +} + +-(void) resetCavanHeight { + _chartCavanHeight = self.frame.size.height - _chartMarginBottom - _chartMarginTop; + if (!_showLabel) { + _chartCavanHeight = self.frame.size.height - 2 * _yLabelHeight; + _chartCavanWidth = self.frame.size.width; + //_chartMargin = chartData.inflexionPointWidth; + _xLabelWidth = (_chartCavanWidth / ([_xLabels count])); + } +} + +- (void)recreatePointLayers { + for (PNLineChartData *chartData in _chartData) { + // create as many chart line layers as there are data-lines + [self.chartLineArray addObject:[NSMutableArray new]]; + + // create point + CAShapeLayer *pointLayer = [CAShapeLayer layer]; + pointLayer.strokeColor = [[chartData.color colorWithAlphaComponent:chartData.alpha] CGColor]; + pointLayer.lineCap = kCALineCapRound; + pointLayer.lineJoin = kCALineJoinBevel; + pointLayer.fillColor = nil; + pointLayer.lineWidth = chartData.lineWidth; + [self.layer addSublayer:pointLayer]; + [self.chartPointArray addObject:pointLayer]; + } +} + +- (void)prepareYLabelsWithData:(NSArray *)data { + CGFloat yMax = 0.0f; + CGFloat yMin = MAXFLOAT; + NSMutableArray *yLabelsArray = [NSMutableArray new]; + + for (PNLineChartData *chartData in data) { + // create as many chart line layers as there are data-lines + + for (NSUInteger i = 0; i < chartData.itemCount; i++) { + CGFloat yValue = chartData.getData(i).y; + [yLabelsArray addObject:[NSString stringWithFormat:@"%2f", yValue]]; + yMax = fmaxf(yMax, yValue); + yMin = fminf(yMin, yValue); + } + } + + + if (_yValueMin == -FLT_MAX) { + _yValueMin = (_yFixedValueMin > -FLT_MAX) ? _yFixedValueMin : yMin; + } + if (_yValueMax == -FLT_MAX) { + _yValueMax = (CGFloat) ((_yFixedValueMax > -FLT_MAX) ? _yFixedValueMax : yMax + yMax / 10.0); + } + + if (_showGenYLabels) { + [self setYLabels]; + } + +} + +#pragma mark - Update Chart Data + +- (void)updateChartData:(NSArray *)data { + _chartData = data; + + [self prepareYLabelsWithData:data]; + + [self calculateChartPath:_chartPath + andPointsPath:_pointPath + andPathKeyPoints:_pathPoints + andPathStartEndPoints:_endPointsOfPath + andProgressLinePathsColors:_progressLinePathsColors]; + + for (NSUInteger lineIndex = 0; lineIndex < self.chartData.count; lineIndex++) { + + CAShapeLayer *chartLine = (CAShapeLayer *) self.chartLineArray[lineIndex]; + CAShapeLayer *pointLayer = (CAShapeLayer *) self.chartPointArray[lineIndex]; + + + NSArray<UIBezierPath *> *progressLines = _chartPath[lineIndex]; + UIBezierPath *pointPath = _pointPath[lineIndex]; + + for(UIBezierPath *progressLine in progressLines) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; + pathAnimation.fromValue = (id) chartLine.path; + pathAnimation.toValue = (__bridge id) [progressLine CGPath]; + pathAnimation.duration = 0.5f; + pathAnimation.autoreverses = NO; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + [chartLine addAnimation:pathAnimation forKey:@"animationKey"]; + chartLine.path = progressLine.CGPath; + } + + CABasicAnimation *pointPathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; + pointPathAnimation.fromValue = (id) pointLayer.path; + pointPathAnimation.toValue = (__bridge id) [pointPath CGPath]; + pointPathAnimation.duration = 0.5f; + pointPathAnimation.autoreverses = NO; + pointPathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + [pointLayer addAnimation:pointPathAnimation forKey:@"animationKey"]; + + pointLayer.path = pointPath.CGPath; + + + } + +} + +#define IOS7_OR_LATER [[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 + +- (void)drawRect:(CGRect)rect { + if (self.isShowCoordinateAxis) { + CGFloat yAxisOffset = 10.f; + + CGContextRef ctx = UIGraphicsGetCurrentContext(); + UIGraphicsPopContext(); + UIGraphicsPushContext(ctx); + CGContextSetLineWidth(ctx, self.axisWidth); + CGContextSetStrokeColorWithColor(ctx, [self.axisColor CGColor]); + + CGFloat xAxisWidth = CGRectGetWidth(rect) - (_chartMarginLeft + _chartMarginRight) / 2; + CGFloat yAxisHeight = _chartMarginBottom + _chartCavanHeight; + + // draw coordinate axis + CGContextMoveToPoint(ctx, _chartMarginBottom + yAxisOffset, 0); + CGContextAddLineToPoint(ctx, _chartMarginBottom + yAxisOffset, yAxisHeight); + CGContextAddLineToPoint(ctx, xAxisWidth, yAxisHeight); + CGContextStrokePath(ctx); + + // draw y axis arrow + CGContextMoveToPoint(ctx, _chartMarginBottom + yAxisOffset - 3, 6); + CGContextAddLineToPoint(ctx, _chartMarginBottom + yAxisOffset, 0); + CGContextAddLineToPoint(ctx, _chartMarginBottom + yAxisOffset + 3, 6); + CGContextStrokePath(ctx); + + // draw x axis arrow + CGContextMoveToPoint(ctx, xAxisWidth - 6, yAxisHeight - 3); + CGContextAddLineToPoint(ctx, xAxisWidth, yAxisHeight); + CGContextAddLineToPoint(ctx, xAxisWidth - 6, yAxisHeight + 3); + CGContextStrokePath(ctx); + + if (self.showLabel) { + + // draw x axis separator + CGPoint point; + for (NSUInteger i = 0; i < [self.xLabels count]; i++) { + point = CGPointMake(2 * _chartMarginLeft + (i * _xLabelWidth), _chartMarginBottom + _chartCavanHeight); + CGContextMoveToPoint(ctx, point.x, point.y - 2); + CGContextAddLineToPoint(ctx, point.x, point.y); + CGContextStrokePath(ctx); + } + + // draw y axis separator + CGFloat yStepHeight = _chartCavanHeight / _yLabelNum; + for (NSUInteger i = 0; i < [self.xLabels count]; i++) { + point = CGPointMake(_chartMarginBottom + yAxisOffset, (_chartCavanHeight - i * yStepHeight + _yLabelHeight / 2)); + CGContextMoveToPoint(ctx, point.x, point.y); + CGContextAddLineToPoint(ctx, point.x + 2, point.y); + CGContextStrokePath(ctx); + } + } + + UIFont *font = [UIFont systemFontOfSize:11]; + + // draw y unit + if ([self.yUnit length]) { + CGFloat height = [PNLineChart sizeOfString:self.yUnit withWidth:30.f font:font].height; + CGRect drawRect = CGRectMake(_chartMarginLeft + 10 + 5, 0, 30.f, height); + [self drawTextInContext:ctx text:self.yUnit inRect:drawRect font:font color:self.yLabelColor]; + } + + // draw x unit + if ([self.xUnit length]) { + CGFloat height = [PNLineChart sizeOfString:self.xUnit withWidth:30.f font:font].height; + CGRect drawRect = CGRectMake(CGRectGetWidth(rect) - _chartMarginLeft + 5, _chartMarginBottom + _chartCavanHeight - height / 2, 25.f, height); + [self drawTextInContext:ctx text:self.xUnit inRect:drawRect font:font color:self.xLabelColor]; + } + } + if (self.showYGridLines) { + CGContextRef ctx = UIGraphicsGetCurrentContext(); + CGFloat yAxisOffset = _showLabel ? 10.f : 0.0f; + CGPoint point; + CGFloat yStepHeight = _chartCavanHeight / _yLabelNum; + if (self.yGridLinesColor) { + CGContextSetStrokeColorWithColor(ctx, self.yGridLinesColor.CGColor); + } else { + CGContextSetStrokeColorWithColor(ctx, [UIColor lightGrayColor].CGColor); + } + for (NSUInteger i = 0; i < _yLabelNum; i++) { + point = CGPointMake(_chartMarginLeft + yAxisOffset, (_chartCavanHeight - i * yStepHeight + _yLabelHeight / 2)); + CGContextMoveToPoint(ctx, point.x, point.y); + // add dotted style grid + CGFloat dash[] = {6, 5}; + // dot diameter is 20 points + CGContextSetLineWidth(ctx, 0.5); + CGContextSetLineCap(ctx, kCGLineCapRound); + CGContextSetLineDash(ctx, 0.0, dash, 2); + CGContextAddLineToPoint(ctx, CGRectGetWidth(rect) - _chartMarginLeft + 5, point.y); + CGContextStrokePath(ctx); + } + } + + [super drawRect:rect]; +} + +#pragma mark private methods + +/* + * helper function that maps a y value ( from chartData) to + * a position in the chart ( between _yValueMin and _yValueMax) + */ +- (CGFloat)yValuePositionInLineChart:(CGFloat)y { + CGFloat innerGrade; + if (!(_yValueMax - _yValueMin)) { + innerGrade = 0.5; + } else { + innerGrade = ((CGFloat) y - _yValueMin) / (_yValueMax - _yValueMin); + } + return _chartCavanHeight - (innerGrade * _chartCavanHeight) - (_yLabelHeight / 2) + _chartMarginTop; +} + +/** + * return array of segments which represents the color and path + * for each segments. + * for instance if p1.y=1 and p2.y=10 + * and rangeColor = use blue for 2<y<3 and red for 4<y<6 + * then this function divides the space between p1 and p2 into three segments + * segment #1 : 1-2 : default color + * segment #2 : 2-3 : blue + * segment #3 : 3-4 : default color + * segment #4 : 4-6 : red + * segment #5: 6-10 : default color + * + * keep in mind that the rangeColors values are based on the chartData so it needs to + * convert those values to coordinates which are valid between yValueMin and yValueMax + * + * in order to find whether there is an overlap between any of the rangeColors and the + * p1-p2 it uses NSIntersectionRange to intersect their yValues. + * + * @param p1 + * @param p2 + * @param rangeColors + * @param defaultColor + * @return + */ +- (NSArray *)colorRangesBetweenP1:(CGPoint)p1 P2:(CGPoint)p2 + rangeColors:(NSArray<PNLineChartColorRange *> *)rangeColors + defaultColor:(UIColor *)defaultColor { + if (rangeColors && rangeColors.count > 0 && p2.x > p1.x) { + PNLineChartColorRange *colorForRangeInfo = [[rangeColors firstObject] copy]; + NSArray *remainingRanges = nil; + if (rangeColors.count > 1) { + remainingRanges = [rangeColors subarrayWithRange:NSMakeRange(1, rangeColors.count - 1)]; + } + // tRange : convert the rangeColors.range values to value between yValueMin and yValueMax + CGFloat transformedStart = [self yValuePositionInLineChart:(CGFloat) + colorForRangeInfo.range.location]; + CGFloat transformedEnd = [self yValuePositionInLineChart:(CGFloat) + (colorForRangeInfo.range.location + colorForRangeInfo.range.length)]; + + NSRange pathRange = NSMakeRange((NSUInteger) fmin(p1.y, p2.y), (NSUInteger) fabs(p2.y - p1.y)); + NSRange tRange = NSMakeRange((NSUInteger) fmin(transformedStart, transformedEnd), + (NSUInteger) fabs(transformedEnd - transformedStart)); + if (NSIntersectionRange(tRange, pathRange).length > 0) { + CGPoint partition1EndPoint; + CGPoint partition2EndPoint; + NSArray *partition1 = @[]; + NSDictionary *partition2 = nil; + NSArray *partition3 = @[]; + if (p2.y >= p1.y) { + partition1EndPoint = CGPointMake([PNLineChart xOfY:(CGFloat) fmax(p1.y, tRange.location) + betweenPoint1:p1 + andPoint2:p2], (CGFloat) fmax(p1.y, tRange.location)); + partition2EndPoint = CGPointMake([PNLineChart xOfY:(CGFloat) fmin(p2.y, tRange.location + tRange.length) + betweenPoint1:p1 + andPoint2:p2], (CGFloat) fmin(p2.y, tRange.location + tRange.length)); + } else { + partition1EndPoint = CGPointMake([PNLineChart xOfY:(CGFloat) fmin(p1.y, tRange.location + tRange.length) + betweenPoint1:p1 + andPoint2:p2], (CGFloat) fmin(p1.y, tRange.location + tRange.length)); + partition2EndPoint = CGPointMake([PNLineChart xOfY:(CGFloat) fmax(p2.y, tRange.location) + betweenPoint1:p1 + andPoint2:p2], (CGFloat) fmax(p2.y, tRange.location)); + } + if (p1.y != partition1EndPoint.y) { + partition1 = [self colorRangesBetweenP1:p1 + P2:partition1EndPoint + rangeColors:remainingRanges + defaultColor:defaultColor]; + } + partition2 = @{ + @"color": colorForRangeInfo.color, + @"from": [NSValue valueWithCGPoint:partition1EndPoint], + @"to": [NSValue valueWithCGPoint:partition2EndPoint]}; + if (p2.y != partition2EndPoint.y) { + partition3 = [self colorRangesBetweenP1:partition2EndPoint + P2:p2 + rangeColors:remainingRanges + defaultColor:defaultColor]; + } + return [[partition1 arrayByAddingObject:partition2] arrayByAddingObjectsFromArray:partition3]; + } else { + + return [self colorRangesBetweenP1:p1 + P2:p2 + rangeColors:remainingRanges + defaultColor:defaultColor]; + } + } else { + return @[@{ + @"color": defaultColor, + @"from": [NSValue valueWithCGPoint:p1], + @"to": [NSValue valueWithCGPoint:p2]}]; + } +} + + +- (void)setupDefaultValues { + [super setupDefaultValues]; + // Initialization code + self.backgroundColor = [UIColor whiteColor]; + self.clipsToBounds = YES; + self.chartLineArray = [NSMutableArray new]; + _showLabel = YES; + _showGenYLabels = YES; + _pathPoints = [[NSMutableArray alloc] init]; + _endPointsOfPath = [[NSMutableArray alloc] init]; + self.userInteractionEnabled = YES; + + _yFixedValueMin = -FLT_MAX; + _yFixedValueMax = -FLT_MAX; + _yValueMax = -FLT_MAX; + _yValueMin = -FLT_MAX; + _yLabelNum = 5; + _yLabelHeight = [[[[PNChartLabel alloc] init] font] pointSize]; + +// _chartMargin = 40; + + _chartMarginLeft = 25.0; + _chartMarginRight = 25.0; + _chartMarginTop = 25.0; + _chartMarginBottom = 25.0; + + _yLabelFormat = @"%1.f"; + + _chartCavanWidth = self.frame.size.width - _chartMarginLeft - _chartMarginRight; + _chartCavanHeight = self.frame.size.height - _chartMarginBottom - _chartMarginTop; + + // Coordinate Axis Default Values + _showCoordinateAxis = NO; + _axisColor = [UIColor colorWithRed:0.4f green:0.4f blue:0.4f alpha:1.f]; + _axisWidth = 1.f; + + // do not create curved line chart by default + _showSmoothLines = NO; + +} + +#pragma mark - tools + ++ (CGSize)sizeOfString:(NSString *)text withWidth:(float)width font:(UIFont *)font { + CGSize size = CGSizeMake(width, MAXFLOAT); + + if ([text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) { + NSDictionary *tdic = @{NSFontAttributeName: font}; + size = [text boundingRectWithSize:size + options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading + attributes:tdic + context:nil].size; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + size = [text sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByCharWrapping]; +#pragma clang diagnostic pop + } + + return size; +} + ++ (CGPoint)midPointBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2 { + return CGPointMake((point1.x + point2.x) / 2, (point1.y + point2.y) / 2); +} + ++ (CGFloat)xOfY:(CGFloat)y betweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2 { + CGFloat m = (point2.y - point1.y) / (point2.x - point1.x); + // formulate = y - y1 = m (x - x1) = mx - mx1 -> mx = y - y1 + mx1 -> + // x = (y - y1 + mx1) / m + return (y - point1.y + m * point1.x) / m; +} + + ++ (CGPoint)controlPointBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2 { + CGPoint controlPoint = [self midPointBetweenPoint1:point1 andPoint2:point2]; + CGFloat diffY = abs((int) (point2.y - controlPoint.y)); + if (point1.y < point2.y) + controlPoint.y += diffY; + else if (point1.y > point2.y) + controlPoint.y -= diffY; + return controlPoint; +} + +- (void)drawTextInContext:(CGContextRef)ctx text:(NSString *)text inRect:(CGRect)rect font:(UIFont *)font color:(UIColor *)color { + if (IOS7_OR_LATER) { + NSMutableParagraphStyle *priceParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + priceParagraphStyle.lineBreakMode = NSLineBreakByTruncatingTail; + priceParagraphStyle.alignment = NSTextAlignmentLeft; + + if (color != nil) { + [text drawInRect:rect + withAttributes:@{NSParagraphStyleAttributeName: priceParagraphStyle, NSFontAttributeName: font, + NSForegroundColorAttributeName: color}]; + } else { + [text drawInRect:rect + withAttributes:@{NSParagraphStyleAttributeName: priceParagraphStyle, NSFontAttributeName: font}]; + } + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [text drawInRect:rect + withFont:font + lineBreakMode:NSLineBreakByTruncatingTail + alignment:NSTextAlignmentLeft]; +#pragma clang diagnostic pop + } +} + +- (NSString *)formatYLabel:(double)value { + + if (self.yLabelBlockFormatter) { + return self.yLabelBlockFormatter((CGFloat) value); + } else { + if (!self.thousandsSeparator) { + NSString *format = self.yLabelFormat ?: @"%1.f"; + return [NSString stringWithFormat:format, value]; + } + + NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; + [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; + [numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; + return [numberFormatter stringFromNumber:@(value)]; + } +} + +- (UIView *)getLegendWithMaxWidth:(CGFloat)mWidth { + if ([self.chartData count] < 1) { + return nil; + } + + /* This is a short line that refers to the chart data */ + CGFloat legendLineWidth = 40; + + /* x and y are the coordinates of the starting point of each legend item */ + CGFloat x = 0; + CGFloat y = 0; + + /* accumulated height */ + CGFloat totalHeight = 0; + CGFloat totalWidth = 0; + + NSMutableArray *legendViews = [[NSMutableArray alloc] init]; + + /* Determine the max width of each legend item */ + CGFloat maxLabelWidth; + if (self.legendStyle == PNLegendItemStyleStacked) { + maxLabelWidth = mWidth - legendLineWidth; + } else { + maxLabelWidth = MAXFLOAT; + } + + /* this is used when labels wrap text and the line + * should be in the middle of the first row */ + CGFloat singleRowHeight = [PNLineChart sizeOfString:@"Test" + withWidth:MAXFLOAT + font:self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]].height; + + NSUInteger counter = 0; + NSUInteger rowWidth = 0; + NSUInteger rowMaxHeight = 0; + + for (PNLineChartData *pdata in self.chartData) { + /* Expected label size*/ + CGSize labelsize = [PNLineChart sizeOfString:pdata.dataTitle + withWidth:maxLabelWidth + font:self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]]; + + /* draw lines */ + if ((rowWidth + labelsize.width + legendLineWidth > mWidth) && (self.legendStyle == PNLegendItemStyleSerial)) { + rowWidth = 0; + x = 0; + y += rowMaxHeight; + rowMaxHeight = 0; + } + rowWidth += labelsize.width + legendLineWidth; + totalWidth = self.legendStyle == PNLegendItemStyleSerial ? fmaxf(rowWidth, totalWidth) : fmaxf(totalWidth, labelsize.width + legendLineWidth); + + /* If there is inflection decorator, the line is composed of two lines + * and this is the space that separates two lines in order to put inflection + * decorator */ + + CGFloat inflexionWidthSpacer = pdata.inflexionPointStyle == PNLineChartPointStyleTriangle ? pdata.inflexionPointWidth / 2 : pdata.inflexionPointWidth; + + CGFloat halfLineLength; + + if (pdata.inflexionPointStyle != PNLineChartPointStyleNone) { + halfLineLength = (CGFloat) ((legendLineWidth * 0.8 - inflexionWidthSpacer) / 2); + } else { + halfLineLength = (CGFloat) (legendLineWidth * 0.8); + } + + UIView *line = [[UIView alloc] initWithFrame:CGRectMake((CGFloat) (x + legendLineWidth * 0.1), y + (singleRowHeight - pdata.lineWidth) / 2, halfLineLength, pdata.lineWidth)]; + + line.backgroundColor = pdata.color; + line.alpha = pdata.alpha; + [legendViews addObject:line]; + + if (pdata.inflexionPointStyle != PNLineChartPointStyleNone) { + line = [[UIView alloc] initWithFrame:CGRectMake((CGFloat) (x + legendLineWidth * 0.1 + halfLineLength + inflexionWidthSpacer), y + (singleRowHeight - pdata.lineWidth) / 2, halfLineLength, pdata.lineWidth)]; + line.backgroundColor = pdata.color; + line.alpha = pdata.alpha; + [legendViews addObject:line]; + } + + // Add inflexion type + UIColor *inflexionPointColor = pdata.inflexionPointColor; + if (!inflexionPointColor) { + inflexionPointColor = pdata.color; + } + [legendViews addObject:[self drawInflexion:pdata.inflexionPointWidth + center:CGPointMake(x + legendLineWidth / 2, y + singleRowHeight / 2) + strokeWidth:pdata.lineWidth + inflexionStyle:pdata.inflexionPointStyle + andColor:inflexionPointColor + andAlpha:pdata.alpha]]; + + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x + legendLineWidth, y, labelsize.width, labelsize.height)]; + label.text = pdata.dataTitle; + label.textColor = self.legendFontColor ? self.legendFontColor : [UIColor blackColor]; + label.font = self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]; + label.lineBreakMode = NSLineBreakByWordWrapping; + label.numberOfLines = 0; + + rowMaxHeight = (NSUInteger) fmaxf(rowMaxHeight, labelsize.height); + x += self.legendStyle == PNLegendItemStyleStacked ? 0 : labelsize.width + legendLineWidth; + y += self.legendStyle == PNLegendItemStyleStacked ? labelsize.height : 0; + + + totalHeight = self.legendStyle == PNLegendItemStyleSerial ? fmaxf(totalHeight, rowMaxHeight + y) : totalHeight + labelsize.height; + + [legendViews addObject:label]; + counter++; + } + + UIView *legend = [[UIView alloc] initWithFrame:CGRectMake(0, 0, mWidth, totalHeight)]; + + for (UIView *v in legendViews) { + [legend addSubview:v]; + } + return legend; +} + + +- (UIImageView *)drawInflexion:(CGFloat)size center:(CGPoint)center strokeWidth:(CGFloat)sw inflexionStyle:(PNLineChartPointStyle)type andColor:(UIColor *)color andAlpha:(CGFloat)alfa { + //Make the size a little bigger so it includes also border stroke + CGSize aSize = CGSizeMake(size + sw, size + sw); + + + UIGraphicsBeginImageContextWithOptions(aSize, NO, 0.0); + CGContextRef context = UIGraphicsGetCurrentContext(); + + + if (type == PNLineChartPointStyleCircle) { + CGContextAddArc(context, (size + sw) / 2, (size + sw) / 2, size / 2, 0, (CGFloat) (M_PI * 2), YES); + } else if (type == PNLineChartPointStyleSquare) { + CGContextAddRect(context, CGRectMake(sw / 2, sw / 2, size, size)); + } else if (type == PNLineChartPointStyleTriangle) { + CGContextMoveToPoint(context, sw / 2, size + sw / 2); + CGContextAddLineToPoint(context, size + sw / 2, size + sw / 2); + CGContextAddLineToPoint(context, size / 2 + sw / 2, sw / 2); + CGContextAddLineToPoint(context, sw / 2, size + sw / 2); + CGContextClosePath(context); + } + + //Set some stroke properties + CGContextSetLineWidth(context, sw); + CGContextSetAlpha(context, alfa); + CGContextSetStrokeColorWithColor(context, color.CGColor); + + //Finally draw + CGContextDrawPath(context, kCGPathStroke); + + //now get the image from the context + UIImage *squareImage = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + + //// Translate origin + CGFloat originX = (CGFloat) (center.x - (size + sw) / 2.0); + CGFloat originY = (CGFloat) (center.y - (size + sw) / 2.0); + + UIImageView *squareImageView = [[UIImageView alloc] initWithImage:squareImage]; + [squareImageView setFrame:CGRectMake(originX, originY, size + sw, size + sw)]; + return squareImageView; +} + +#pragma mark setter and getter + +- (CATextLayer *)createPointLabelFor:(CGFloat)grade pointCenter:(CGPoint)pointCenter width:(CGFloat)width withChartData:(PNLineChartData *)chartData { + CATextLayer *textLayer = [[CATextLayer alloc] init]; + [textLayer setAlignmentMode:kCAAlignmentCenter]; + [textLayer setForegroundColor:[chartData.pointLabelColor CGColor]]; + [textLayer setBackgroundColor:self.backgroundColor.CGColor]; +// [textLayer setBackgroundColor:[self.backgroundColor colorWithAlphaComponent:0.8].CGColor]; +// [textLayer setCornerRadius:(CGFloat) (textLayer.fontSize / 8.0)]; + + if (chartData.pointLabelFont != nil) { + [textLayer setFont:(__bridge CFTypeRef) (chartData.pointLabelFont)]; + textLayer.fontSize = [chartData.pointLabelFont pointSize]; + } + + CGFloat textHeight = (CGFloat) (textLayer.fontSize * 1.1); + // FIXME: convert the grade to string and use its length instead of hardcoding 8 + CGFloat textWidth = width * 8; + CGFloat textStartPosY; + + textStartPosY = pointCenter.y - textLayer.fontSize; + + [self.layer addSublayer:textLayer]; + + if (chartData.pointLabelFormat != nil) { + [textLayer setString:[[NSString alloc] initWithFormat:chartData.pointLabelFormat, grade]]; + } else { + [textLayer setString:[[NSString alloc] initWithFormat:_yLabelFormat, grade]]; + } + + [textLayer setFrame:CGRectMake(0, 0, textWidth, textHeight)]; + [textLayer setPosition:CGPointMake(pointCenter.x, textStartPosY)]; + textLayer.contentsScale = [UIScreen mainScreen].scale; + + return textLayer; +} + +- (CABasicAnimation *)fadeAnimation { + CABasicAnimation *fadeAnimation = nil; + if (self.displayAnimated) { + fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + fadeAnimation.fromValue = @0.0F; + fadeAnimation.toValue = @1.0F; + fadeAnimation.duration = 2.0; + } + return fadeAnimation; +} + +- (CABasicAnimation *)pathAnimation { + if (self.displayAnimated && !_pathAnimation) { + _pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + _pathAnimation.duration = 1.0; + _pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + _pathAnimation.fromValue = @0.0f; + _pathAnimation.toValue = @1.0f; + } + if(!self.displayAnimated) { + _pathAnimation = nil; + } + return _pathAnimation; +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNLineChartData.h b/PNChartdemo/PNChartdemo/PNChart/PNLineChartData.h new file mode 100755 index 0000000..2158e8e --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNLineChartData.h @@ -0,0 +1,61 @@ +// +// Created by J��rg Polakowski on 14/12/13. +// Copyright (c) 2013 kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +typedef NS_ENUM(NSUInteger, PNLineChartPointStyle) { + PNLineChartPointStyleNone = 0, + PNLineChartPointStyleCircle = 1, + PNLineChartPointStyleSquare = 3, + PNLineChartPointStyleTriangle = 4 +}; + +@class PNLineChartDataItem; + +typedef PNLineChartDataItem *(^LCLineChartDataGetter)(NSUInteger item); + +@interface PNLineChartColorRange : NSObject<NSCopying> + +@property(nonatomic) NSRange range; +@property(nonatomic) BOOL inclusive; +@property(nonatomic, retain) UIColor *color; + +- (id)initWithRange:(NSRange)range color:(UIColor *)color; + +@end + +@interface PNLineChartData : NSObject + +@property (strong) UIColor *color; +@property (nonatomic) CGFloat alpha; +@property NSUInteger itemCount; +@property (copy) LCLineChartDataGetter getData; +@property (strong, nonatomic) NSString *dataTitle; + +@property (nonatomic) BOOL showPointLabel; +@property (nonatomic) UIColor *pointLabelColor; +@property (nonatomic) UIFont *pointLabelFont; +@property (nonatomic) NSString *pointLabelFormat; + +@property (nonatomic, assign) PNLineChartPointStyle inflexionPointStyle; +@property (nonatomic) UIColor *inflexionPointColor; + +/** + * if rangeColor is set and the lineChartData values are within any + * of the given range then use the rangeColor.color otherwise use + * self.color for the rest of the graph + */ +@property(strong) NSArray<PNLineChartColorRange *> *rangeColors; + +/** + * If PNLineChartPointStyle is circle, this returns the circle's diameter. + * If PNLineChartPointStyle is square, each point is a square with each side equal in length to this value. + */ +@property (nonatomic, assign) CGFloat inflexionPointWidth; + +@property (nonatomic, assign) CGFloat lineWidth; + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNLineChartData.m b/PNChartdemo/PNChartdemo/PNChart/PNLineChartData.m new file mode 100755 index 0000000..bbf2647 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNLineChartData.m @@ -0,0 +1,54 @@ +// +// Created by J��rg Polakowski on 14/12/13. +// Copyright (c) 2013 kevinzhow. All rights reserved. +// + +#import "PNLineChartData.h" + + +@implementation PNLineChartColorRange + +- (id)initWithRange:(NSRange)range color:(UIColor *)color { + self = [super init]; + if (self) { + self.range = range; + self.color = color; + } + return self; +} + + +- (id)copyWithZone:(NSZone *)zone { + PNLineChartColorRange *copy = [[self class] allocWithZone:zone]; + copy.color = self.color; + copy.range = self.range; + return copy; +} + +@end + +@implementation PNLineChartData + +- (id)init +{ + self = [super init]; + if (self) { + [self setupDefaultValues]; + } + + return self; +} + +- (void)setupDefaultValues +{ + _inflexionPointStyle = PNLineChartPointStyleNone; + _inflexionPointWidth = 6.f; + _lineWidth = 2.f; + _alpha = 1.f; + _showPointLabel = NO; + _pointLabelColor = [UIColor blackColor]; + _pointLabelFormat = @"%1.f"; + _rangeColors = nil; +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNLineChartDataItem.h b/PNChartdemo/PNChartdemo/PNChart/PNLineChartDataItem.h new file mode 100755 index 0000000..ad2d23d --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNLineChartDataItem.h @@ -0,0 +1,17 @@ +// +// Created by J��rg Polakowski on 14/12/13. +// Copyright (c) 2013 kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +@interface PNLineChartDataItem : NSObject + ++ (PNLineChartDataItem *)dataItemWithY:(CGFloat)y; ++ (PNLineChartDataItem *)dataItemWithY:(CGFloat)y andRawY:(CGFloat)rawY; + +@property (readonly) CGFloat y; // should be within the y range +@property (readonly) CGFloat rawY; // this is the raw value, used for point label. + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNLineChartDataItem.m b/PNChartdemo/PNChartdemo/PNChart/PNLineChartDataItem.m new file mode 100755 index 0000000..1beea91 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNLineChartDataItem.m @@ -0,0 +1,38 @@ +// +// Created by J��rg Polakowski on 14/12/13. +// Copyright (c) 2013 kevinzhow. All rights reserved. +// + +#import "PNLineChartDataItem.h" + +@interface PNLineChartDataItem () + +- (id)initWithY:(CGFloat)y andRawY:(CGFloat)rawY; + +@property (readwrite) CGFloat y; // should be within the y range +@property (readwrite) CGFloat rawY; // this is the raw value, used for point label. + +@end + +@implementation PNLineChartDataItem + ++ (PNLineChartDataItem *)dataItemWithY:(CGFloat)y +{ + return [[PNLineChartDataItem alloc] initWithY:y andRawY:y]; +} + ++ (PNLineChartDataItem *)dataItemWithY:(CGFloat)y andRawY:(CGFloat)rawY { + return [[PNLineChartDataItem alloc] initWithY:y andRawY:rawY]; +} + +- (id)initWithY:(CGFloat)y andRawY:(CGFloat)rawY +{ + if ((self = [super init])) { + self.y = y; + self.rawY = rawY; + } + + return self; +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNPieChart.h b/PNChartdemo/PNChartdemo/PNChart/PNPieChart.h new file mode 100755 index 0000000..204afb5 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNPieChart.h @@ -0,0 +1,68 @@ +// +// PNPieChart.h +// PNChartDemo +// +// Created by Hang Zhang on 14-5-5. +// Copyright (c) 2014��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "PNPieChartDataItem.h" +#import "PNGenericChart.h" +#import "PNChartDelegate.h" + +@interface PNPieChart : PNGenericChart + +- (id)initWithFrame:(CGRect)frame items:(NSArray *)items; + +@property (nonatomic, readonly) NSArray *items; + +/** Default is 18-point Avenir Medium. */ +@property (nonatomic) UIFont *descriptionTextFont; + +/** Default is white. */ +@property (nonatomic) UIColor *descriptionTextColor; + +/** Default is black, with an alpha of 0.4. */ +@property (nonatomic) UIColor *descriptionTextShadowColor; + +/** Default is CGSizeMake(0, 1). */ +@property (nonatomic) CGSize descriptionTextShadowOffset; + +/** Default is 1.0. */ +@property (nonatomic) NSTimeInterval duration; + +/** Show only values, this is useful when legend is present */ +@property (nonatomic) BOOL showOnlyValues; + +/** Show absolute values not relative i.e. percentages */ +@property (nonatomic) BOOL showAbsoluteValues; + +/** Hide percentage labels less than cutoff value */ +@property (nonatomic, assign) CGFloat labelPercentageCutoff; + +/** Default YES. */ +@property (nonatomic) BOOL shouldHighlightSectorOnTouch; + +/** Current outer radius. Override recompute() to change this. **/ +@property (nonatomic) CGFloat outerCircleRadius; + +/** Current inner radius. Override recompute() to change this. **/ +@property (nonatomic) CGFloat innerCircleRadius; + +@property (nonatomic, weak) id<PNChartDelegate> delegate; + +/** Update chart items. Does not update chart itself. */ +- (void)updateChartData:(NSArray *)data; + +/** Multiple selection */ +@property (nonatomic, assign) BOOL enableMultipleSelection; + +/** show only tiles, not values or percentage */ +@property (nonatomic) BOOL hideValues; + +- (void)strokeChart; + +- (void)recompute; + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNPieChart.m b/PNChartdemo/PNChartdemo/PNChart/PNPieChart.m new file mode 100755 index 0000000..ae9ab7a --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNPieChart.m @@ -0,0 +1,508 @@ +// +// PNPieChart.m +// PNChartDemo +// +// Created by Hang Zhang on 14-5-5. +// Copyright (c) 2014��� kevinzhow. All rights reserved. +// + +#import "PNPieChart.h" +//needed for the expected label size +#import "PNLineChart.h" + +@interface PNPieChart() + +@property (nonatomic) NSArray *items; +@property (nonatomic) NSArray *endPercentages; + +@property (nonatomic) UIView *contentView; +@property (nonatomic) CAShapeLayer *pieLayer; +@property (nonatomic) NSMutableArray *descriptionLabels; +@property (strong, nonatomic) CAShapeLayer *sectorHighlight; + +@property (nonatomic, strong) NSMutableDictionary *selectedItems; + +- (void)loadDefault; + +- (UILabel *)descriptionLabelForItemAtIndex:(NSUInteger)index; +- (PNPieChartDataItem *)dataItemForIndex:(NSUInteger)index; +- (CGFloat)startPercentageForItemAtIndex:(NSUInteger)index; +- (CGFloat)endPercentageForItemAtIndex:(NSUInteger)index; +- (CGFloat)ratioForItemAtIndex:(NSUInteger)index; + +- (CAShapeLayer *)newCircleLayerWithRadius:(CGFloat)radius + borderWidth:(CGFloat)borderWidth + fillColor:(UIColor *)fillColor + borderColor:(UIColor *)borderColor + startPercentage:(CGFloat)startPercentage + endPercentage:(CGFloat)endPercentage; + + +@end + + +@implementation PNPieChart + +-(id)initWithFrame:(CGRect)frame items:(NSArray *)items{ + self = [self initWithFrame:frame]; + if(self){ + _items = [NSArray arrayWithArray:items]; + [self baseInit]; + } + + return self; +} + +- (void)awakeFromNib{ + [super awakeFromNib]; + [self baseInit]; +} + +- (void)baseInit{ + _selectedItems = [NSMutableDictionary dictionary]; + //������������������,���������������������������������������������,������������������,���������������,���������������view���������,��������������������������������� + + CGFloat minimal = (CGRectGetWidth(self.bounds) < CGRectGetHeight(self.bounds)) ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds); + + _outerCircleRadius = minimal / 2; + _innerCircleRadius = minimal / 6; +// _outerCircleRadius = CGRectGetWidth(self.bounds) / 2; +// _innerCircleRadius = CGRectGetWidth(self.bounds) / 6; + _descriptionTextColor = [UIColor whiteColor]; + _descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:18.0]; + _descriptionTextShadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; + _descriptionTextShadowOffset = CGSizeMake(0, 1); + _duration = 1.0; + _shouldHighlightSectorOnTouch = YES; + _enableMultipleSelection = NO; + _hideValues = NO; + + [super setupDefaultValues]; + [self loadDefault]; +} + +- (void)loadDefault{ + __block CGFloat currentTotal = 0; + CGFloat total = [[self.items valueForKeyPath:@"@sum.value"] floatValue]; + NSMutableArray *endPercentages = [NSMutableArray new]; + [_items enumerateObjectsUsingBlock:^(PNPieChartDataItem *item, NSUInteger idx, BOOL *stop) { + if (total == 0){ + [endPercentages addObject:@(1.0 / _items.count * (idx + 1))]; + }else{ + currentTotal += item.value; + [endPercentages addObject:@(currentTotal / total)]; + } + }]; + self.endPercentages = [endPercentages copy]; + + [_contentView removeFromSuperview]; + _contentView = [[UIView alloc] initWithFrame:self.bounds]; + [self addSubview:_contentView]; + _descriptionLabels = [NSMutableArray new]; + + _pieLayer = [CAShapeLayer layer]; + [_contentView.layer addSublayer:_pieLayer]; + +} + +/** Override this to change how inner attributes are computed. **/ +- (void)recompute { + + //������ + CGFloat minimal = (CGRectGetWidth(self.bounds) < CGRectGetHeight(self.bounds)) ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds); + self.outerCircleRadius = minimal / 2; + self.innerCircleRadius = minimal / 6; +} + +#pragma mark - + +- (void)strokeChart{ + [self loadDefault]; + [self recompute]; + + PNPieChartDataItem *currentItem; + for (int i = 0; i < _items.count; i++) { + currentItem = [self dataItemForIndex:i]; + + + CGFloat startPercentage = [self startPercentageForItemAtIndex:i]; + CGFloat endPercentage = [self endPercentageForItemAtIndex:i]; + + CGFloat radius = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius) / 2; + CGFloat borderWidth = _outerCircleRadius - _innerCircleRadius; + + CAShapeLayer *currentPieLayer = [self newCircleLayerWithRadius:radius + borderWidth:borderWidth + fillColor:[UIColor clearColor] + borderColor:currentItem.color + startPercentage:startPercentage + endPercentage:endPercentage]; + [_pieLayer addSublayer:currentPieLayer]; + } + + [self maskChart]; + + for (int i = 0; i < _items.count; i++) { + UILabel *descriptionLabel = [self descriptionLabelForItemAtIndex:i]; + [_contentView addSubview:descriptionLabel]; + [_descriptionLabels addObject:descriptionLabel]; + } + + [self addAnimationIfNeeded]; +} + +- (UILabel *)descriptionLabelForItemAtIndex:(NSUInteger)index{ + PNPieChartDataItem *currentDataItem = [self dataItemForIndex:index]; + CGFloat distance = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius) / 2; + CGFloat centerPercentage = ([self startPercentageForItemAtIndex:index] + [self endPercentageForItemAtIndex:index])/ 2; + CGFloat rad = centerPercentage * 2 * M_PI; + + UILabel *descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 80)]; + NSString *titleText = currentDataItem.textDescription; + + NSString *titleValue; + + if (self.showAbsoluteValues) { + titleValue = [NSString stringWithFormat:@"%.0f",currentDataItem.value]; + }else{ + titleValue = [NSString stringWithFormat:@"%.0f%%",[self ratioForItemAtIndex:index] * 100]; + } + + if (self.hideValues) + descriptionLabel.text = titleText; + else if(!titleText || self.showOnlyValues) + descriptionLabel.text = titleValue; + else { + NSString* str = [titleValue stringByAppendingString:[NSString stringWithFormat:@"\n%@",titleText]]; + descriptionLabel.text = str ; + } + + //If value is less than cutoff, show no label + if ([self ratioForItemAtIndex:index] < self.labelPercentageCutoff ) + { + descriptionLabel.text = nil; + } + + CGPoint center = CGPointMake(_outerCircleRadius + distance * sin(rad), + _outerCircleRadius - distance * cos(rad)); + + descriptionLabel.font = _descriptionTextFont; + CGSize labelSize = [descriptionLabel.text sizeWithAttributes:@{NSFontAttributeName:descriptionLabel.font}]; + descriptionLabel.frame = CGRectMake(descriptionLabel.frame.origin.x, descriptionLabel.frame.origin.y, + descriptionLabel.frame.size.width, labelSize.height); + descriptionLabel.numberOfLines = 0; + descriptionLabel.textColor = _descriptionTextColor; + descriptionLabel.shadowColor = _descriptionTextShadowColor; + descriptionLabel.shadowOffset = _descriptionTextShadowOffset; + descriptionLabel.textAlignment = NSTextAlignmentCenter; + descriptionLabel.center = center; + descriptionLabel.alpha = 0; + descriptionLabel.backgroundColor = [UIColor clearColor]; + return descriptionLabel; +} + +- (void)updateChartData:(NSArray *)items { + self.items = items; +} + +- (PNPieChartDataItem *)dataItemForIndex:(NSUInteger)index{ + return self.items[index]; +} + +- (CGFloat)startPercentageForItemAtIndex:(NSUInteger)index{ + if(index == 0){ + return 0; + } + + return [_endPercentages[index - 1] floatValue]; +} + +- (CGFloat)endPercentageForItemAtIndex:(NSUInteger)index{ + return [_endPercentages[index] floatValue]; +} + +- (CGFloat)ratioForItemAtIndex:(NSUInteger)index{ + return [self endPercentageForItemAtIndex:index] - [self startPercentageForItemAtIndex:index]; +} + +#pragma mark private methods + +- (CAShapeLayer *)newCircleLayerWithRadius:(CGFloat)radius + borderWidth:(CGFloat)borderWidth + fillColor:(UIColor *)fillColor + borderColor:(UIColor *)borderColor + startPercentage:(CGFloat)startPercentage + endPercentage:(CGFloat)endPercentage{ + CAShapeLayer *circle = [CAShapeLayer layer]; + + CGPoint center = CGPointMake(CGRectGetMidX(self.bounds),CGRectGetMidY(self.bounds)); + + UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center + radius:radius + startAngle:-M_PI_2 + endAngle:M_PI_2 * 3 + clockwise:YES]; + + circle.fillColor = fillColor.CGColor; + circle.strokeColor = borderColor.CGColor; + circle.strokeStart = startPercentage; + circle.strokeEnd = endPercentage; + circle.lineWidth = borderWidth; + circle.path = path.CGPath; + + return circle; +} + +- (void)maskChart{ + CGFloat radius = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius) / 2; + CGFloat borderWidth = _outerCircleRadius - _innerCircleRadius; + CAShapeLayer *maskLayer = [self newCircleLayerWithRadius:radius + borderWidth:borderWidth + fillColor:[UIColor clearColor] + borderColor:[UIColor blackColor] + startPercentage:0 + endPercentage:1]; + + _pieLayer.mask = maskLayer; +} + +- (void)addAnimationIfNeeded{ + if (self.displayAnimated) { + CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + animation.duration = _duration; + animation.fromValue = @0; + animation.toValue = @1; + animation.delegate = self; + animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + animation.removedOnCompletion = YES; + [_pieLayer.mask addAnimation:animation forKey:@"circleAnimation"]; + } + else { + // Add description labels since no animation is required + [_descriptionLabels enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [obj setAlpha:1]; + }]; + } +} + +- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{ + [_descriptionLabels enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [UIView animateWithDuration:0.2 animations:^(){ + [obj setAlpha:1]; + }]; + }]; +} + +- (void)didTouchAt:(CGPoint)touchLocation +{ + CGPoint circleCenter = CGPointMake(_contentView.bounds.size.width/2, _contentView.bounds.size.height/2); + + CGFloat distanceFromCenter = sqrtf(powf((touchLocation.y - circleCenter.y),2) + powf((touchLocation.x - circleCenter.x),2)); + + if (distanceFromCenter < _innerCircleRadius) { + if ([self.delegate respondsToSelector:@selector(didUnselectPieItem)]) { + [self.delegate didUnselectPieItem]; + } + [self.sectorHighlight removeFromSuperlayer]; + return; + } + + CGFloat percentage = [self findPercentageOfAngleInCircle:circleCenter fromPoint:touchLocation]; + int index = 0; + while (percentage > [self endPercentageForItemAtIndex:index]) { + index ++; + } + + if ([self.delegate respondsToSelector:@selector(userClickedOnPieIndexItem:)]) { + [self.delegate userClickedOnPieIndexItem:index]; + } + + if (self.shouldHighlightSectorOnTouch) + { + if (!self.enableMultipleSelection) + { + if (self.sectorHighlight) + [self.sectorHighlight removeFromSuperlayer]; + } + + PNPieChartDataItem *currentItem = [self dataItemForIndex:index]; + + CGFloat red,green,blue,alpha; + UIColor *old = currentItem.color; + [old getRed:&red green:&green blue:&blue alpha:&alpha]; + alpha /= 2; + UIColor *newColor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; + + CGFloat startPercentage = [self startPercentageForItemAtIndex:index]; + CGFloat endPercentage = [self endPercentageForItemAtIndex:index]; + + self.sectorHighlight = [self newCircleLayerWithRadius:_outerCircleRadius + 5 + borderWidth:10 + fillColor:[UIColor clearColor] + borderColor:newColor + startPercentage:startPercentage + endPercentage:endPercentage]; + + if (self.enableMultipleSelection) + { + NSString *dictIndex = [NSString stringWithFormat:@"%d", index]; + CAShapeLayer *indexShape = [self.selectedItems valueForKey:dictIndex]; + if (indexShape) + { + [indexShape removeFromSuperlayer]; + [self.selectedItems removeObjectForKey:dictIndex]; + } + else + { + [self.selectedItems setObject:self.sectorHighlight forKey:dictIndex]; + [_contentView.layer addSublayer:self.sectorHighlight]; + } + } + else + { + [_contentView.layer addSublayer:self.sectorHighlight]; + } + } +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + for (UITouch *touch in touches) { + CGPoint touchLocation = [touch locationInView:_contentView]; + [self didTouchAt:touchLocation]; + } +} + +- (CGFloat) findPercentageOfAngleInCircle:(CGPoint)center fromPoint:(CGPoint)reference{ + //Find angle of line Passing In Reference And Center + CGFloat angleOfLine = atanf((reference.y - center.y) / (reference.x - center.x)); + CGFloat percentage = (angleOfLine + M_PI/2)/(2 * M_PI); + return (reference.x - center.x) > 0 ? percentage : percentage + .5; +} + +- (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth{ + if ([self.items count] < 1) { + return nil; + } + + /* This is a small circle that refers to the chart data */ + CGFloat legendCircle = 16; + + CGFloat hSpacing = 0; + + CGFloat beforeLabel = legendCircle + hSpacing; + + /* x and y are the coordinates of the starting point of each legend item */ + CGFloat x = 0; + CGFloat y = 0; + + /* accumulated width and height */ + CGFloat totalWidth = 0; + CGFloat totalHeight = 0; + + NSMutableArray *legendViews = [[NSMutableArray alloc] init]; + + /* Determine the max width of each legend item */ + CGFloat maxLabelWidth; + if (self.legendStyle == PNLegendItemStyleStacked) { + maxLabelWidth = mWidth - beforeLabel; + }else{ + maxLabelWidth = MAXFLOAT; + } + + /* this is used when labels wrap text and the line + * should be in the middle of the first row */ + CGFloat singleRowHeight = [PNLineChart sizeOfString:@"Test" + withWidth:MAXFLOAT + font:self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]].height; + + NSUInteger counter = 0; + NSUInteger rowWidth = 0; + NSUInteger rowMaxHeight = 0; + + for (PNPieChartDataItem *pdata in self.items) { + /* Expected label size*/ + CGSize labelsize = [PNLineChart sizeOfString:pdata.textDescription + withWidth:maxLabelWidth + font:self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]]; + + if ((rowWidth + labelsize.width + beforeLabel > mWidth)&&(self.legendStyle == PNLegendItemStyleSerial)) { + rowWidth = 0; + x = 0; + y += rowMaxHeight; + rowMaxHeight = 0; + } + rowWidth += labelsize.width + beforeLabel; + totalWidth = self.legendStyle == PNLegendItemStyleSerial ? fmaxf(rowWidth, totalWidth) : fmaxf(totalWidth, labelsize.width + beforeLabel); + // Add inflexion type + [legendViews addObject:[self drawInflexion:legendCircle * .6 + center:CGPointMake(x + legendCircle / 2, y + singleRowHeight / 2) + andColor:pdata.color]]; + + + UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x + beforeLabel, y, labelsize.width, labelsize.height)]; + label.text = pdata.textDescription; + label.textColor = self.legendFontColor ? self.legendFontColor : [UIColor blackColor]; + label.font = self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]; + label.lineBreakMode = NSLineBreakByWordWrapping; + label.numberOfLines = 0; + + + rowMaxHeight = fmaxf(rowMaxHeight, labelsize.height); + x += self.legendStyle == PNLegendItemStyleStacked ? 0 : labelsize.width + beforeLabel; + y += self.legendStyle == PNLegendItemStyleStacked ? labelsize.height : 0; + + + totalHeight = self.legendStyle == PNLegendItemStyleSerial ? fmaxf(totalHeight, rowMaxHeight + y) : totalHeight + labelsize.height; + [legendViews addObject:label]; + counter ++; + } + + UIView *legend = [[UIView alloc] initWithFrame:CGRectMake(0, 0, totalWidth, totalHeight)]; + + for (UIView* v in legendViews) { + [legend addSubview:v]; + } + return legend; +} + + +- (UIImageView*)drawInflexion:(CGFloat)size center:(CGPoint)center andColor:(UIColor*)color +{ + //Make the size a little bigger so it includes also border stroke + CGSize aSize = CGSizeMake(size, size); + + + UIGraphicsBeginImageContextWithOptions(aSize, NO, 0.0); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextAddArc(context, size/2, size/ 2, size/2, 0, M_PI*2, YES); + + + //Set some fill color + CGContextSetFillColorWithColor(context, color.CGColor); + + //Finally draw + CGContextDrawPath(context, kCGPathFill); + + //now get the image from the context + UIImage *squareImage = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + + //// Translate origin + CGFloat originX = center.x - (size) / 2.0; + CGFloat originY = center.y - (size) / 2.0; + + UIImageView *squareImageView = [[UIImageView alloc]initWithImage:squareImage]; + [squareImageView setFrame:CGRectMake(originX, originY, size, size)]; + return squareImageView; +} + +/* Redraw the chart on autolayout */ +-(void)layoutSubviews { + [super layoutSubviews]; + [self strokeChart]; +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNPieChartDataItem.h b/PNChartdemo/PNChartdemo/PNChart/PNPieChartDataItem.h new file mode 100755 index 0000000..08d48e9 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNPieChartDataItem.h @@ -0,0 +1,25 @@ +// +// PNPieChartDataItem.h +// PNChartDemo +// +// Created by Hang Zhang on 14-5-5. +// Copyright (c) 2014��� kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +@interface PNPieChartDataItem : NSObject + ++ (instancetype)dataItemWithValue:(CGFloat)value + color:(UIColor*)color; + ++ (instancetype)dataItemWithValue:(CGFloat)value + color:(UIColor*)color + description:(NSString *)description; + +@property (nonatomic) CGFloat value; +@property (nonatomic) UIColor *color; +@property (nonatomic) NSString *textDescription; + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNPieChartDataItem.m b/PNChartdemo/PNChartdemo/PNChart/PNPieChartDataItem.m new file mode 100755 index 0000000..4bda818 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNPieChartDataItem.m @@ -0,0 +1,38 @@ +// +// PNPieChartDataItem.m +// PNChartDemo +// +// Created by Hang Zhang on 14-5-5. +// Copyright (c) 2014��� kevinzhow. All rights reserved. +// + +#import "PNPieChartDataItem.h" +#import <UIKit/UIKit.h> + +@implementation PNPieChartDataItem + + ++ (instancetype)dataItemWithValue:(CGFloat)value + color:(UIColor*)color{ + PNPieChartDataItem *item = [PNPieChartDataItem new]; + item.value = value; + item.color = color; + return item; +} + ++ (instancetype)dataItemWithValue:(CGFloat)value + color:(UIColor*)color + description:(NSString *)description { + PNPieChartDataItem *item = [PNPieChartDataItem dataItemWithValue:value color:color]; + item.textDescription = description; + return item; +} + +- (void)setValue:(CGFloat)value{ + NSAssert(value >= 0, @"value should >= 0"); + if (value != _value){ + _value = value; + } +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNRadarChart.h b/PNChartdemo/PNChartdemo/PNChart/PNRadarChart.h new file mode 100755 index 0000000..87871d0 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNRadarChart.h @@ -0,0 +1,52 @@ +// +// PNRadarChart.h +// PNChartDemo +// +// Created by Lei on 15/7/1. +// Copyright (c) 2015��� kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import "PNGenericChart.h" +#import "PNRadarChartDataItem.h" + +#define MAXCIRCLE 20 + +typedef NS_ENUM(NSUInteger, PNRadarChartLabelStyle) { + PNRadarChartLabelStyleCircle = 0, + PNRadarChartLabelStyleHorizontal, + PNRadarChartLabelStyleHidden, +}; + +@interface PNRadarChart : PNGenericChart + +-(id)initWithFrame:(CGRect)frame items:(NSArray *)items valueDivider:(CGFloat)unitValue; +/** + *Draws the chart in an animated fashion. + */ +-(void)strokeChart; + +/** Array of `RadarChartDataItem` objects, one for each corner. */ +@property (nonatomic) NSArray *chartData; +/** The unit of this chart ,default is 1 */ +@property (nonatomic) CGFloat valueDivider; +/** The maximum for the range of values to display on the chart */ +@property (nonatomic) CGFloat maxValue; +/** Default is gray. */ +@property (nonatomic) UIColor *webColor; +/** Default is green , with an alpha of 0.7 */ +@property (nonatomic) UIColor *plotColor; +/** Default is black */ +@property (nonatomic) UIColor *fontColor; +/** Default is orange */ +@property (nonatomic) UIColor *graduationColor; +/** Default is 15 */ +@property (nonatomic) CGFloat fontSize; +/** Controls the labels display style that around chart */ +@property (nonatomic, assign) PNRadarChartLabelStyle labelStyle; +/** Tap the label will display detail value ,default is YES. */ +@property (nonatomic, assign) BOOL isLabelTouchable; +/** is show graduation on the chart ,default is NO. */ +@property (nonatomic, assign) BOOL isShowGraduation; + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNRadarChart.m b/PNChartdemo/PNChartdemo/PNChart/PNRadarChart.m new file mode 100755 index 0000000..9dace2b --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNRadarChart.m @@ -0,0 +1,373 @@ +// +// PNRadarChart.m +// PNChartDemo +// +// Created by Lei on 15/7/1. +// Copyright (c) 2015��� kevinzhow. All rights reserved. +// + +#import "PNRadarChart.h" + +@interface PNRadarChart() + +@property (nonatomic) CGFloat centerX; +@property (nonatomic) CGFloat centerY; +@property (nonatomic) NSMutableArray *pointsToWebArrayArray; +@property (nonatomic) NSMutableArray *pointsToPlotArray; +@property (nonatomic) UILabel *detailLabel; +@property (nonatomic) CGFloat lengthUnit; +@property (nonatomic) CAShapeLayer *chartPlot; + +@end + + +@implementation PNRadarChart + +- (id)initWithFrame:(CGRect)frame items:(NSArray *)items valueDivider:(CGFloat)unitValue { + self=[super initWithFrame:frame]; + if (self) { + self.backgroundColor = [UIColor clearColor]; + self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + + //Public iVar + if ([items count]< 3)//At least three corners of A polygon ,If the count of items is less than 3 will add 3 default values + { + NSLog( @"At least three items!"); + NSArray *defaultArray = @[[PNRadarChartDataItem dataItemWithValue:0 description:@"Default"], + [PNRadarChartDataItem dataItemWithValue:0 description:@"Default"], + [PNRadarChartDataItem dataItemWithValue:0 description:@"Default"], + ]; + defaultArray = [defaultArray arrayByAddingObjectsFromArray:items]; + _chartData = [NSArray arrayWithArray:defaultArray]; + }else{ + _chartData = [NSArray arrayWithArray:items]; + } + _valueDivider = unitValue; + _maxValue = 1; + _webColor = [UIColor grayColor]; + _plotColor = [UIColor colorWithRed:.4 green:.8 blue:.4 alpha:.7]; + _fontColor = [UIColor blackColor]; + _graduationColor = [UIColor orangeColor]; + _fontSize = 15; + _labelStyle = PNRadarChartLabelStyleHorizontal; + _isLabelTouchable = YES; + _isShowGraduation = NO; + + //Private iVar + _centerX = frame.size.width/2; + _centerY = frame.size.height/2; + _pointsToWebArrayArray = [NSMutableArray array]; + _pointsToPlotArray = [NSMutableArray array]; + _lengthUnit = 0; + _chartPlot = [CAShapeLayer layer]; + _chartPlot.lineCap = kCALineCapButt; + _chartPlot.lineWidth = 1.0; + [self.layer addSublayer:_chartPlot]; + + [super setupDefaultValues]; + //init detailLabel + _detailLabel = [[UILabel alloc] init]; + _detailLabel.backgroundColor = [UIColor colorWithRed:.9 green:.9 blue:.1 alpha:.9]; + _detailLabel.textAlignment = NSTextAlignmentCenter; + _detailLabel.textColor = [UIColor colorWithWhite:1 alpha:1]; + _detailLabel.font = [UIFont systemFontOfSize:15]; + [_detailLabel setHidden:YES]; + [self addSubview:_detailLabel]; + + [self strokeChart]; + } + return self; +} + +#pragma mark - main +- (void)calculateChartPoints { + [_pointsToPlotArray removeAllObjects]; + [_pointsToWebArrayArray removeAllObjects]; + + //init Descriptions , Values and Angles. + NSMutableArray *descriptions = [NSMutableArray array]; + NSMutableArray *values = [NSMutableArray array]; + NSMutableArray *angles = [NSMutableArray array]; + for (int i=0;i<_chartData.count;i++) { + PNRadarChartDataItem *item = (PNRadarChartDataItem *)[_chartData objectAtIndex:i]; + [descriptions addObject:item.textDescription]; + [values addObject:[NSNumber numberWithFloat:item.value]]; + CGFloat angleValue = (float)i/(float)[_chartData count]*2*M_PI; + [angles addObject:[NSNumber numberWithFloat:angleValue]]; + } + + //calculate all the lengths + _maxValue = [self getMaxValueFromArray:values]; + CGFloat margin = 0; + if (_labelStyle==PNRadarChartLabelStyleCircle) { + margin = MIN(_centerX , _centerY)*3/10; + }else if (_labelStyle==PNRadarChartLabelStyleHorizontal) { + margin = [self getMaxWidthLabelFromArray:descriptions withFontSize:_fontSize]; + } + CGFloat maxLength = ceil(MIN(_centerX, _centerY) - margin); + int plotCircles = (_maxValue/_valueDivider); + if (plotCircles > MAXCIRCLE) { + NSLog(@"Circle number is higher than max"); + plotCircles = MAXCIRCLE; + _valueDivider = _maxValue/plotCircles; + } + _lengthUnit = maxLength/plotCircles; + NSArray *lengthArray = [self getLengthArrayWithCircleNum:(int)plotCircles]; + + //get all the points and plot + for (NSNumber *lengthNumber in lengthArray) { + CGFloat length = [lengthNumber floatValue]; + [_pointsToWebArrayArray addObject:[self getWebPointWithLength:length angleArray:angles]]; + } + int section = 0; + for (id value in values) { + CGFloat valueFloat = [value floatValue]; + if (valueFloat>_maxValue) { + NSString *reason = [NSString stringWithFormat:@"Value number is higher than max -value: %f - maxValue: %f",valueFloat,_maxValue]; + @throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil]; + return; + } + + CGFloat length = valueFloat/_maxValue*maxLength; + CGFloat angle = [[angles objectAtIndex:section] floatValue]; + CGFloat x = _centerX +length*cos(angle); + CGFloat y = _centerY +length*sin(angle); + NSValue* point = [NSValue valueWithCGPoint:CGPointMake(x, y)]; + [_pointsToPlotArray addObject:point]; + section++; + } + //set the labels + [self drawLabelWithMaxLength:maxLength labelArray:descriptions angleArray:angles]; + + } +#pragma mark - Draw + +- (void)drawRect:(CGRect)rect { + // Drawing backgound + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextClearRect(context, rect); + int section = 0; + //circles + for(NSArray *pointArray in _pointsToWebArrayArray){ + //plot backgound + CGContextRef graphContext = UIGraphicsGetCurrentContext(); + CGContextBeginPath(graphContext); + CGPoint beginPoint = [[pointArray objectAtIndex:0] CGPointValue]; + CGContextMoveToPoint(graphContext, beginPoint.x, beginPoint.y); + for(NSValue* pointValue in pointArray){ + CGPoint point = [pointValue CGPointValue]; + CGContextAddLineToPoint(graphContext, point.x, point.y); + } + CGContextAddLineToPoint(graphContext, beginPoint.x, beginPoint.y); + CGContextSetStrokeColorWithColor(graphContext, _webColor.CGColor); + CGContextStrokePath(graphContext); + + } + //cuts + NSArray *largestPointArray = [_pointsToWebArrayArray lastObject]; + for (NSValue *pointValue in largestPointArray){ + section++; + if (section==1&&_isShowGraduation)continue; + + CGContextRef graphContext = UIGraphicsGetCurrentContext(); + CGContextBeginPath(graphContext); + CGContextMoveToPoint(graphContext, _centerX, _centerY); + CGPoint point = [pointValue CGPointValue]; + CGContextAddLineToPoint(graphContext, point.x, point.y); + CGContextSetStrokeColorWithColor(graphContext, _webColor.CGColor); + CGContextStrokePath(graphContext); + } + + +} + +- (void)strokeChart { + + [self calculateChartPoints]; + [self setNeedsDisplay]; + [_detailLabel setHidden:YES]; + + //Draw plot + [_chartPlot removeAllAnimations]; + UIBezierPath *plotline = [UIBezierPath bezierPath]; + CGPoint beginPoint = [[_pointsToPlotArray objectAtIndex:0] CGPointValue]; + [plotline moveToPoint:CGPointMake(beginPoint.x, beginPoint.y)]; + for(NSValue *pointValue in _pointsToPlotArray){ + CGPoint point = [pointValue CGPointValue]; + [plotline addLineToPoint:CGPointMake(point.x ,point.y)]; + + } + [plotline setLineWidth:1]; + [plotline setLineCapStyle:kCGLineCapButt]; + + _chartPlot.path = plotline.CGPath; + + _chartPlot.fillColor = _plotColor.CGColor; + + [self addAnimationIfNeeded]; + [self showGraduation]; +} + +#pragma mark - Helper + +- (void)drawLabelWithMaxLength:(CGFloat)maxLength labelArray:(NSArray *)labelArray angleArray:(NSArray *)angleArray { + //set labels + int labelTag = 121; + while (true) { + UIView *label = [self viewWithTag:labelTag]; + if(!label)break; + [label removeFromSuperview]; + } + int section = 0; + CGFloat labelLength = maxLength + maxLength/10; + + for (NSString *labelString in labelArray) { + CGFloat angle = [[angleArray objectAtIndex:section] floatValue]; + CGFloat x = _centerX + labelLength *cos(angle); + CGFloat y = _centerY + labelLength *sin(angle); + + UILabel *label = [[UILabel alloc] init] ; + label.backgroundColor = [UIColor clearColor]; + label.font = [UIFont systemFontOfSize:_fontSize]; + label.text = labelString; + label.tag = labelTag; + CGSize detailSize = [labelString sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:_fontSize]}]; + + switch (_labelStyle) { + case PNRadarChartLabelStyleCircle: + label.frame = CGRectMake(x-5*_fontSize/2, y-_fontSize/2, 5*_fontSize, _fontSize); + label.transform = CGAffineTransformMakeRotation(((float)section/[labelArray count])*(2*M_PI)+M_PI_2); + label.textAlignment = NSTextAlignmentCenter; + + break; + case PNRadarChartLabelStyleHorizontal: + if (x<_centerX) { + label.frame = CGRectMake(x-detailSize.width, y-detailSize.height/2, detailSize.width, detailSize.height); + label.textAlignment = NSTextAlignmentRight; + }else{ + label.frame = CGRectMake(x, y-detailSize.height/2, detailSize.width , detailSize.height); + label.textAlignment = NSTextAlignmentLeft; + } + break; + case PNRadarChartLabelStyleHidden: + [label setHidden:YES]; + break; + default: + break; + } + [label sizeToFit]; + + label.userInteractionEnabled = YES; + UITapGestureRecognizer *tapLabelGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapLabel:)]; + [label addGestureRecognizer:tapLabelGesture]; + [self addSubview:label]; + + section ++; + } + +} + +- (void)tapLabel:(UITapGestureRecognizer *)recognizer { + UILabel *label=(UILabel*)recognizer.view; + _detailLabel.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y-30, 50, 25); + for (PNRadarChartDataItem *item in _chartData) { + if ([label.text isEqualToString:item.textDescription]) { + _detailLabel.text = [NSString stringWithFormat:@"%.2f", item.value]; + break; + } + } + [_detailLabel setHidden:NO]; + +} + +- (void)showGraduation { + int labelTag = 112; + while (true) { + UIView *label = [self viewWithTag:labelTag]; + if(!label)break; + [label removeFromSuperview]; + } + int section = 0; + for (NSArray *pointsArray in _pointsToWebArrayArray) { + section++; + CGPoint labelPoint = [[pointsArray objectAtIndex:0] CGPointValue]; + UILabel *graduationLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelPoint.x-_lengthUnit, labelPoint.y-_lengthUnit*5/8, _lengthUnit*5/8, _lengthUnit)]; + graduationLabel.adjustsFontSizeToFitWidth = YES; + graduationLabel.tag = labelTag; + graduationLabel.font = [UIFont systemFontOfSize:ceil(_lengthUnit)]; + graduationLabel.textColor = [UIColor orangeColor]; + graduationLabel.text = [NSString stringWithFormat:@"%.0f",_valueDivider*section]; + [self addSubview:graduationLabel]; + if (_isShowGraduation) { + [graduationLabel setHidden:NO]; + }else{ + [graduationLabel setHidden:YES];} + } + +} + +- (NSArray *)getWebPointWithLength:(CGFloat)length angleArray:(NSArray *)angleArray { + NSMutableArray *pointArray = [NSMutableArray array]; + for (NSNumber *angleNumber in angleArray) { + CGFloat angle = [angleNumber floatValue]; + CGFloat x = _centerX + length*cos(angle); + CGFloat y = _centerY + length*sin(angle); + [pointArray addObject:[NSValue valueWithCGPoint:CGPointMake(x,y)]]; + } + return pointArray; + +} + +- (NSArray *)getLengthArrayWithCircleNum:(int)plotCircles { + NSMutableArray *lengthArray = [NSMutableArray array]; + CGFloat length = 0; + for (int i = 0; i < plotCircles; i++) { + length += _lengthUnit; + [lengthArray addObject:[NSNumber numberWithFloat:length]]; + } + return lengthArray; +} + +- (CGFloat)getMaxWidthLabelFromArray:(NSArray *)keyArray withFontSize:(CGFloat)size { + CGFloat maxWidth = 0; + for (NSString *str in keyArray) { + CGSize detailSize = [str sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:_fontSize]}]; + maxWidth = MAX(maxWidth, detailSize.width); + } + return maxWidth; +} + +- (CGFloat)getMaxValueFromArray:(NSArray *)valueArray { + CGFloat max = _maxValue; + for (NSNumber *valueNum in valueArray) { + CGFloat valueFloat = [valueNum floatValue]; + max = MAX(valueFloat, max); + } + return ceil(max); +} + +- (void)addAnimationIfNeeded +{ + if (self.displayAnimated) { + CABasicAnimation *animateScale = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; + animateScale.fromValue = [NSNumber numberWithFloat:0.f]; + animateScale.toValue = [NSNumber numberWithFloat:1.0f]; + + CABasicAnimation *animateMove = [CABasicAnimation animationWithKeyPath:@"position"]; + animateMove.fromValue = [NSValue valueWithCGPoint:CGPointMake(_centerX, _centerY)]; + animateMove.toValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)]; + + CABasicAnimation *animateAlpha = [CABasicAnimation animationWithKeyPath:@"opacity"]; + animateAlpha.fromValue = [NSNumber numberWithFloat:0.f]; + + CAAnimationGroup *aniGroup = [CAAnimationGroup animation]; + aniGroup.duration = 1.f; + aniGroup.repeatCount = 1; + aniGroup.animations = [NSArray arrayWithObjects:animateScale,animateMove,animateAlpha, nil]; + aniGroup.removedOnCompletion = YES; + + [_chartPlot addAnimation:aniGroup forKey:nil]; + } +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNRadarChartDataItem.h b/PNChartdemo/PNChartdemo/PNChart/PNRadarChartDataItem.h new file mode 100755 index 0000000..e67429c --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNRadarChartDataItem.h @@ -0,0 +1,19 @@ +// +// PNRadarChartDataItem.h +// PNChartDemo +// +// Created by Lei on 15/7/1. +// Copyright (c) 2015��� kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> + +@interface PNRadarChartDataItem : NSObject + ++ (instancetype)dataItemWithValue:(CGFloat)value + description:(NSString *)description; + +@property (nonatomic) CGFloat value; +@property (nonatomic,copy) NSString *textDescription; + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNRadarChartDataItem.m b/PNChartdemo/PNChartdemo/PNChart/PNRadarChartDataItem.m new file mode 100755 index 0000000..218ef6e --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNRadarChartDataItem.m @@ -0,0 +1,29 @@ +// +// PNRadarChartDataItem.m +// PNChartDemo +// +// Created by Lei on 15/7/1. +// Copyright (c) 2015��� kevinzhow. All rights reserved. +// + +#import "PNRadarChartDataItem.h" + +@implementation PNRadarChartDataItem + ++ (instancetype)dataItemWithValue:(CGFloat)value + description:(NSString *)description { + PNRadarChartDataItem *item = [PNRadarChartDataItem new]; + item.value = value; + item.textDescription = description; + return item; +} + +- (void)setValue:(CGFloat)value { + if (value < 0) { + value = 0; + NSLog(@"Value value can not be negative"); + } + _value = value; +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNScatterChart.h b/PNChartdemo/PNChartdemo/PNChart/PNScatterChart.h new file mode 100755 index 0000000..aa4eab8 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNScatterChart.h @@ -0,0 +1,69 @@ +// +// PNScatterChart.h +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import <QuartzCore/QuartzCore.h> +#import "PNChartDelegate.h" +#import "PNGenericChart.h" +#import "PNScatterChartData.h" +#import "PNScatterChartDataItem.h" + +@interface PNScatterChart : PNGenericChart + +@property (nonatomic, retain) id<PNChartDelegate> delegate; + +/** Array of `ScatterChartData` objects, one for each line. */ +@property (nonatomic) NSArray *chartData; + +/** Controls whether to show the coordinate axis. Default is NO. */ +@property (nonatomic, getter = isShowCoordinateAxis) BOOL showCoordinateAxis; +@property (nonatomic) UIColor *axisColor; +@property (nonatomic) CGFloat axisWidth; + +/** String formatter for float values in x-axis/y-axis labels. If not set, defaults to @"%1.f" */ +@property (nonatomic, strong) NSString *xLabelFormat; +@property (nonatomic, strong) NSString *yLabelFormat; + +/** Default is true. */ +@property (nonatomic) BOOL showLabel; + +/** Default is 18-point Avenir Medium. */ +@property (nonatomic) UIFont *descriptionTextFont; + +/** Default is white. */ +@property (nonatomic) UIColor *descriptionTextColor; + +/** Default is black, with an alpha of 0.4. */ +@property (nonatomic) UIColor *descriptionTextShadowColor; + +/** Default is CGSizeMake(0, 1). */ +@property (nonatomic) CGSize descriptionTextShadowOffset; + +/** Default is 1.0. */ +@property (nonatomic) NSTimeInterval duration; + +@property (nonatomic) CGFloat AxisX_minValue; +@property (nonatomic) CGFloat AxisX_maxValue; + +@property (nonatomic) CGFloat AxisY_minValue; +@property (nonatomic) CGFloat AxisY_maxValue; + +- (void) setAxisXWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks; +- (void) setAxisYWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks; +- (void) setAxisXLabel:(NSArray *)array; +- (void) setAxisYLabel:(NSArray *)array; +- (void) setup; +- (void) drawLineFromPoint : (CGPoint) startPoint ToPoint : (CGPoint) endPoint WithLineWith : (CGFloat) lineWidth AndWithColor : (UIColor*) color; + +/** + * Update Chart Value + */ + +- (void)updateChartData:(NSArray *)data; + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNScatterChart.m b/PNChartdemo/PNChartdemo/PNChart/PNScatterChart.m new file mode 100755 index 0000000..a16cf85 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNScatterChart.m @@ -0,0 +1,445 @@ +// +// PNScatterChart.m +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import "PNScatterChart.h" +#import "PNColor.h" +#import "PNChartLabel.h" +#import "PNScatterChartData.h" +#import "PNScatterChartDataItem.h" + +@interface PNScatterChart () + +@property (nonatomic, weak) CAShapeLayer *pathLayer; +@property (nonatomic, weak) NSMutableArray *verticalLineLayer; +@property (nonatomic, weak) NSMutableArray *horizentalLinepathLayer; + +@property (nonatomic) CGPoint startPoint; + +@property (nonatomic) CGPoint startPointVectorX; +@property (nonatomic) CGPoint endPointVecotrX; + +@property (nonatomic) CGPoint startPointVectorY; +@property (nonatomic) CGPoint endPointVecotrY; + +@property (nonatomic) CGFloat vectorX_Steps; +@property (nonatomic) CGFloat vectorY_Steps; + +@property (nonatomic) CGFloat vectorX_Size; +@property (nonatomic) CGFloat vectorY_Size; + +@property (nonatomic) NSMutableArray *axisX_labels; +@property (nonatomic) NSMutableArray *axisY_labels; + +@property (nonatomic) int AxisX_partNumber ; +@property (nonatomic) int AxisY_partNumber ; + +@property (nonatomic) CGFloat AxisX_step ; +@property (nonatomic) CGFloat AxisY_step ; + +@property (nonatomic) CGFloat AxisX_Margin; +@property (nonatomic) CGFloat AxisY_Margin; + +@property (nonatomic) BOOL isForUpdate; + +@end + + +@implementation PNScatterChart + +#pragma mark initialization + +- (id)initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder:coder]; + + if (self) { + [self setupDefaultValues]; + } + return self; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + + if (self) { + [self setupDefaultValues]; + } + return self; +} + +- (void) setup +{ + [self vectorXSetup]; + [self vectorYSetup]; +} + +- (void)setupDefaultValues +{ + [super setupDefaultValues]; + + // Initialization code + self.backgroundColor = [UIColor whiteColor]; + self.clipsToBounds = YES; + _showLabel = YES; + _isForUpdate = NO; + self.userInteractionEnabled = YES; + + // Coordinate Axis Default Values + _showCoordinateAxis = YES; + _axisColor = [UIColor colorWithRed:0.4f green:0.4f blue:0.4f alpha:1.f]; + _axisWidth = 1.f; + + // Initialization code + _AxisX_Margin = 30 ; + _AxisY_Margin = 30 ; + +// self.frame = CGRectMake((SCREEN_WIDTH - self.frame.size.width) / 2, 200, self.frame.size.width, self.frame.size.height) ; + self.backgroundColor = [UIColor clearColor]; + + _startPoint.y = self.frame.size.height - self.AxisY_Margin ; + _startPoint.x = self.AxisX_Margin ; + + _axisX_labels = [NSMutableArray array]; + _axisY_labels = [NSMutableArray array]; + + _descriptionTextColor = [UIColor blackColor]; + _descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:9.0]; + _descriptionTextShadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; + _descriptionTextShadowOffset = CGSizeMake(0, 1); + _duration = 1.0; + +} + +#pragma mark calculating axis + +- (void) setAxisXWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks +{ + _AxisX_minValue = minVal ; + _AxisX_maxValue = maxVal ; + _AxisX_partNumber = numberOfTicks - 1; + _AxisX_step = (float)((maxVal - minVal)/_AxisX_partNumber); + + NSString *LabelFormat = self.xLabelFormat ? : @"%1.f"; + CGFloat tempValue = minVal ; + UILabel *label = [[UILabel alloc] init]; + label.text = [NSString stringWithFormat:LabelFormat,minVal] ; + [_axisX_labels addObject:label]; + for (int i = 0 ; i < _AxisX_partNumber; i++) { + tempValue = tempValue + _AxisX_step; + UILabel *tempLabel = [[UILabel alloc] init]; + tempLabel.text = [NSString stringWithFormat:LabelFormat,tempValue] ; + [_axisX_labels addObject:tempLabel]; + } +} + +- (void) setAxisYWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks +{ + _AxisY_minValue = minVal ; + _AxisY_maxValue = maxVal ; + _AxisY_partNumber = numberOfTicks - 1; + _AxisY_step = (float)((maxVal - minVal)/_AxisY_partNumber); + + NSString *LabelFormat = self.yLabelFormat ? : @"%1.f"; + CGFloat tempValue = minVal ; + UILabel *label = [[UILabel alloc] init]; + label.text = [NSString stringWithFormat:LabelFormat,minVal] ; + [_axisY_labels addObject:label]; + for (int i = 0 ; i < _AxisY_partNumber; i++) { + tempValue = tempValue + _AxisY_step; + UILabel *tempLabel = [[UILabel alloc] init]; + tempLabel.text = [NSString stringWithFormat:LabelFormat,tempValue] ; + [_axisY_labels addObject:tempLabel]; + } +} + +- (NSArray*) getAxisMinMax:(NSArray*)xValues +{ + float min = [xValues[0] floatValue]; + float max = [xValues[0] floatValue]; + for (NSNumber *number in xValues) + { + if ([number floatValue] > max) + max = [number floatValue]; + + if ([number floatValue] < min) + min = [number floatValue]; + } + NSArray *result = @[[NSNumber numberWithFloat:min], [NSNumber numberWithFloat:max]]; + + + return result; +} + +- (void)setAxisXLabel:(NSArray *)array { + if(array.count == ++_AxisX_partNumber){ + [_axisX_labels removeAllObjects]; + for(int i=0;i<array.count;i++){ + UILabel *label = [[UILabel alloc] init]; + label.text = [array objectAtIndex:i]; + [_axisX_labels addObject:label]; + } + } +} + +- (void)setAxisYLabel:(NSArray *)array { + if(array.count == ++_AxisY_partNumber){ + [_axisY_labels removeAllObjects]; + for(int i=0;i<array.count;i++){ + UILabel *label = [[UILabel alloc] init]; + label.text = [array objectAtIndex:i]; + [_axisY_labels addObject:label]; + } + } +} + +- (void) vectorXSetup +{ + _AxisX_partNumber += 1; + _vectorX_Size = self.frame.size.width - (_AxisX_Margin) - 15 ; + _vectorX_Steps = (_vectorX_Size) / (_AxisX_partNumber) ; + _endPointVecotrX = CGPointMake(_startPoint.x + _vectorX_Size, _startPoint.y) ; + _startPointVectorX = _startPoint ; +} + +- (void) vectorYSetup +{ + _AxisY_partNumber += 1; + _vectorY_Size = self.frame.size.height - (_AxisY_Margin) - 15; + _vectorY_Steps = (_vectorY_Size) / (_AxisY_partNumber); + _endPointVecotrY = CGPointMake(_startPoint.x, _startPoint.y - _vectorY_Size) ; + _startPointVectorY = _startPoint ; +} + +- (void) showXLabel : (UILabel *) descriptionLabel InPosition : (CGPoint) point +{ + CGRect frame = CGRectMake(point.x, point.y, 30, 10); + descriptionLabel.frame = frame; + descriptionLabel.font = _descriptionTextFont; + descriptionLabel.textColor = _descriptionTextColor; + descriptionLabel.shadowColor = _descriptionTextShadowColor; + descriptionLabel.shadowOffset = _descriptionTextShadowOffset; + descriptionLabel.textAlignment = NSTextAlignmentCenter; + descriptionLabel.backgroundColor = [UIColor clearColor]; + [self addSubview:descriptionLabel]; +} + +- (void)setChartData:(NSArray *)data +{ + __block CGFloat yFinilizeValue , xFinilizeValue; + __block CGFloat yValue , xValue; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + if (self.displayAnimated) { + [NSThread sleepForTimeInterval:1]; + } + // update UI on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + for (PNScatterChartData *chartData in data) { + for (NSUInteger i = 0; i < chartData.itemCount; i++) { + yValue = chartData.getData(i).y; + xValue = chartData.getData(i).x; + if (!(xValue >= _AxisX_minValue && xValue <= _AxisX_maxValue) || !(yValue >= _AxisY_minValue && yValue <= _AxisY_maxValue)) { + NSLog(@"input is not in correct range."); + exit(0); + } + xFinilizeValue = [self mappingIsForAxisX:true WithValue:xValue]; + yFinilizeValue = [self mappingIsForAxisX:false WithValue:yValue]; + CAShapeLayer *shape = [self drawingPointsForChartData:chartData AndWithX:xFinilizeValue AndWithY:yFinilizeValue]; + self.pathLayer = shape ; + [self.layer addSublayer:self.pathLayer]; + + [self addAnimationIfNeeded]; + } + } + }); + }); +} + +- (void)addAnimationIfNeeded{ + + if (self.displayAnimated) { + CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; + pathAnimation.duration = _duration; + pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + pathAnimation.fromValue = @(0.0f); + pathAnimation.toValue = @(1.0f); + pathAnimation.fillMode = kCAFillModeForwards; + self.layer.opacity = 1; + [self.pathLayer addAnimation:pathAnimation forKey:@"fade"]; + } +} + +- (CGFloat) mappingIsForAxisX : (BOOL) isForAxisX WithValue : (CGFloat) value{ + + if (isForAxisX) { + float temp = _startPointVectorX.x + (_vectorX_Steps / 2) ; + CGFloat xPos = temp + (((value - _AxisX_minValue)/_AxisX_step) * _vectorX_Steps) ; + return xPos; + } + else { + float temp = _startPointVectorY.y - (_vectorY_Steps / 2) ; + CGFloat yPos = temp - (((value - _AxisY_minValue) /_AxisY_step) * _vectorY_Steps); + return yPos; + } + return 0; +} + +#pragma mark - Update Chart Data + +- (void)updateChartData:(NSArray *)data +{ + _chartData = data; + + // will be work in future. +} + +#pragma drawing methods + +- (void)drawRect:(CGRect)rect +{ + [super drawRect:rect]; + + CGContextRef context = UIGraphicsGetCurrentContext(); + if (_showCoordinateAxis) { + CGContextSetStrokeColorWithColor(context, [_axisColor CGColor]); + CGContextSetLineWidth(context, _axisWidth); + //drawing x vector + CGContextMoveToPoint(context, _startPoint.x, _startPoint.y); + CGContextAddLineToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); + //drawing y vector + CGContextMoveToPoint(context, _startPoint.x, _startPoint.y); + CGContextAddLineToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); + //drawing x arrow vector + CGContextMoveToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); + CGContextAddLineToPoint(context, _endPointVecotrX.x - 5, _endPointVecotrX.y + 3); + CGContextMoveToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); + CGContextAddLineToPoint(context, _endPointVecotrX.x - 5, _endPointVecotrX.y - 3); + //drawing y arrow vector + CGContextMoveToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); + CGContextAddLineToPoint(context, _endPointVecotrY.x - 3, _endPointVecotrY.y + 5); + CGContextMoveToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); + CGContextAddLineToPoint(context, _endPointVecotrY.x + 3, _endPointVecotrY.y + 5); + } + + if (_showLabel) { + //drawing x steps vector and putting axis x labels + float temp = _startPointVectorX.x + (_vectorX_Steps / 2) ; + for (int i = 0; i < _axisX_labels.count; i++) { + UIBezierPath *path = [UIBezierPath bezierPath]; + [path moveToPoint:CGPointMake(temp, _startPointVectorX.y - 2)]; + [path addLineToPoint:CGPointMake(temp, _startPointVectorX.y + 3)]; + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; + shapeLayer.path = [path CGPath]; + shapeLayer.strokeColor = [_axisColor CGColor]; + shapeLayer.lineWidth = _axisWidth; + shapeLayer.fillColor = [_axisColor CGColor]; + [self.horizentalLinepathLayer addObject:shapeLayer]; + [self.layer addSublayer:shapeLayer]; + UILabel *lb = [_axisX_labels objectAtIndex:i] ; + [self showXLabel:lb InPosition:CGPointMake(temp - 15, _startPointVectorX.y + 10 )]; + temp = temp + _vectorX_Steps ; + } + //drawing y steps vector and putting axis x labels + temp = _startPointVectorY.y - (_vectorY_Steps / 2) ; + for (int i = 0; i < _axisY_labels.count; i++) { + UIBezierPath *path = [UIBezierPath bezierPath]; + [path moveToPoint:CGPointMake(_startPointVectorY.x - 3, temp)]; + [path addLineToPoint:CGPointMake( _startPointVectorY.x + 2, temp)]; + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; + shapeLayer.path = [path CGPath]; + shapeLayer.strokeColor = [_axisColor CGColor]; + shapeLayer.lineWidth = _axisWidth; + shapeLayer.fillColor = [_axisColor CGColor]; + [self.verticalLineLayer addObject:shapeLayer]; + [self.layer addSublayer:shapeLayer]; + UILabel *lb = [_axisY_labels objectAtIndex:i]; + [self showXLabel:lb InPosition:CGPointMake(_startPointVectorY.x - 30, temp - 5)]; + temp = temp - _vectorY_Steps ; + } + } + CGContextDrawPath(context, kCGPathStroke); +} + +- (CAShapeLayer*) drawingPointsForChartData : (PNScatterChartData *) chartData AndWithX : (CGFloat) X AndWithY : (CGFloat) Y +{ + if (chartData.inflexionPointStyle == PNScatterChartPointStyleCircle) { + float radius = chartData.size; + CAShapeLayer *circle = [CAShapeLayer layer]; + // Make a circular shape + circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(X - radius, Y - radius, 2.0*radius, 2.0*radius) + cornerRadius:radius].CGPath; + // Configure the appearence of the circle + circle.fillColor = [chartData.fillColor CGColor]; + circle.strokeColor = [chartData.strokeColor CGColor]; + circle.lineWidth = 1; + + // Add to parent layer + return circle; + } + else if (chartData.inflexionPointStyle == PNScatterChartPointStyleSquare) { + float side = chartData.size; + CAShapeLayer *square = [CAShapeLayer layer]; + // Make a circular shape + square.path = [UIBezierPath bezierPathWithRect:CGRectMake(X - (side/2) , Y - (side/2), side, side)].CGPath ; + // Configure the apperence of the circle + square.fillColor = [chartData.fillColor CGColor]; + square.strokeColor = [chartData.strokeColor CGColor]; + square.lineWidth = 1; + + // Add to parent layer + return square; + } + else { + // you cann add your own scatter chart point here + } + return nil ; +} + +- (void) drawLineFromPoint : (CGPoint) startPoint ToPoint : (CGPoint) endPoint WithLineWith : (CGFloat) lineWidth AndWithColor : (UIColor*) color{ + + // call the same method on a background thread + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + if (self.displayAnimated) { + [NSThread sleepForTimeInterval:2]; + } + // calculating start and end point + __block CGFloat startX = [self mappingIsForAxisX:true WithValue:startPoint.x]; + __block CGFloat startY = [self mappingIsForAxisX:false WithValue:startPoint.y]; + __block CGFloat endX = [self mappingIsForAxisX:true WithValue:endPoint.x]; + __block CGFloat endY = [self mappingIsForAxisX:false WithValue:endPoint.y]; + // update UI on the main thread + dispatch_async(dispatch_get_main_queue(), ^{ + // drawing path between two points + UIBezierPath *path = [UIBezierPath bezierPath]; + [path moveToPoint:CGPointMake(startX, startY)]; + [path addLineToPoint:CGPointMake(endX, endY)]; + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; + shapeLayer.path = [path CGPath]; + shapeLayer.strokeColor = [color CGColor]; + shapeLayer.lineWidth = lineWidth; + shapeLayer.fillColor = [color CGColor]; + // adding animation to path + [self addStrokeEndAnimationIfNeededToLayer:shapeLayer]; + [self.layer addSublayer:shapeLayer]; + }); + }); +} + +- (void)addStrokeEndAnimationIfNeededToLayer:(CAShapeLayer *)shapeLayer{ + + if (self.displayAnimated) { + CABasicAnimation *animateStrokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; + animateStrokeEnd.duration = _duration; + animateStrokeEnd.fromValue = [NSNumber numberWithFloat:0.0f]; + animateStrokeEnd.toValue = [NSNumber numberWithFloat:1.0f]; + [shapeLayer addAnimation:animateStrokeEnd forKey:nil]; + } +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNScatterChartData.h b/PNChartdemo/PNChartdemo/PNChart/PNScatterChartData.h new file mode 100755 index 0000000..40b7b0e --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNScatterChartData.h @@ -0,0 +1,38 @@ +// +// PNScatterChartData.h +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +typedef NS_ENUM(NSUInteger, PNScatterChartPointStyle) { + PNScatterChartPointStyleCircle = 0, + PNScatterChartPointStyleSquare = 1, +}; + +@class PNScatterChartDataItem; + +typedef PNScatterChartDataItem *(^LCScatterChartDataGetter)(NSUInteger item); + +@interface PNScatterChartData : NSObject + +@property (strong) UIColor *fillColor; +@property (strong) UIColor *strokeColor; + +@property NSUInteger itemCount; +@property (copy) LCScatterChartDataGetter getData; + +@property (nonatomic, assign) PNScatterChartPointStyle inflexionPointStyle; + +/** + * If PNLineChartPointStyle is circle, this returns the circle's diameter. + * If PNLineChartPointStyle is square, each point is a square with each side equal in length to this value. + */ +@property (nonatomic, assign) CGFloat size; + + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNScatterChartData.m b/PNChartdemo/PNChartdemo/PNChart/PNScatterChartData.m new file mode 100755 index 0000000..a53a5c2 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNScatterChartData.m @@ -0,0 +1,31 @@ +// +// PNScatterChartData.m +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import "PNScatterChartData.h" + +@implementation PNScatterChartData + +- (id)init +{ + self = [super init]; + if (self) { + [self setupDefaultValues]; + } + + return self; +} + +- (void)setupDefaultValues +{ + _inflexionPointStyle = PNScatterChartPointStyleCircle; + _fillColor = [UIColor grayColor]; + _strokeColor = [UIColor clearColor]; + _size = 3 ; +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNScatterChartDataItem.h b/PNChartdemo/PNChartdemo/PNChart/PNScatterChartDataItem.h new file mode 100755 index 0000000..f38ee4e --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNScatterChartDataItem.h @@ -0,0 +1,19 @@ +// +// PNScatterChartDataItem.h +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +@interface PNScatterChartDataItem : NSObject + ++ (PNScatterChartDataItem *)dataItemWithX:(CGFloat)x AndWithY:(CGFloat)y; + +@property (readonly) CGFloat x; // should be within the x range +@property (readonly) CGFloat y; // should be within the y range + +@end diff --git a/PNChartdemo/PNChartdemo/PNChart/PNScatterChartDataItem.m b/PNChartdemo/PNChartdemo/PNChart/PNScatterChartDataItem.m new file mode 100755 index 0000000..a454459 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChart/PNScatterChartDataItem.m @@ -0,0 +1,37 @@ +// +// PNScatterChartDataItem.m +// PNChartDemo +// +// Created by Alireza Arabi on 12/4/14. +// Copyright (c) 2014 kevinzhow. All rights reserved. +// + +#import "PNScatterChartDataItem.h" + +@interface PNScatterChartDataItem () + +- (id)initWithX:(CGFloat)x AndWithY:(CGFloat)y; + +@property (readwrite) CGFloat x; // should be within the x range +@property (readwrite) CGFloat y; // should be within the y range + +@end + +@implementation PNScatterChartDataItem + ++ (PNScatterChartDataItem *)dataItemWithX:(CGFloat)x AndWithY:(CGFloat)y +{ + return [[PNScatterChartDataItem alloc] initWithX:x AndWithY:y]; +} + +- (id)initWithX:(CGFloat)x AndWithY:(CGFloat)y +{ + if ((self = [super init])) { + self.x = x; + self.y = y; + } + + return self; +} + +@end diff --git a/PNChartdemo/PNChartdemo/PNChartDemo-Prefix.pch b/PNChartdemo/PNChartdemo/PNChartDemo-Prefix.pch new file mode 100755 index 0000000..a0c6545 --- /dev/null +++ b/PNChartdemo/PNChartdemo/PNChartDemo-Prefix.pch @@ -0,0 +1,16 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#import <Availability.h> + +#ifndef __IPHONE_5_0 +#warning "This project uses features only available in iOS SDK 5.0 and later." +#endif + +#ifdef __OBJC__ + #import <UIKit/UIKit.h> + #import <Foundation/Foundation.h> +#endif diff --git a/PNChartdemo/PNChartdemo/UICountingLabel/UICountingLabel.h b/PNChartdemo/PNChartdemo/UICountingLabel/UICountingLabel.h new file mode 100755 index 0000000..3c59ee3 --- /dev/null +++ b/PNChartdemo/PNChartdemo/UICountingLabel/UICountingLabel.h @@ -0,0 +1,36 @@ +#import <Foundation/Foundation.h> +#import <UIKit/UIKit.h> + +typedef NS_ENUM(NSInteger, UILabelCountingMethod) { + UILabelCountingMethodEaseInOut, + UILabelCountingMethodEaseIn, + UILabelCountingMethodEaseOut, + UILabelCountingMethodLinear +}; + +typedef NSString* (^UICountingLabelFormatBlock)(CGFloat value); +typedef NSAttributedString* (^UICountingLabelAttributedFormatBlock)(CGFloat value); + +@interface UICountingLabel : UILabel + +@property (nonatomic, strong) NSString *format; +@property (nonatomic, assign) UILabelCountingMethod method; +@property (nonatomic, assign) NSTimeInterval animationDuration; + +@property (nonatomic, copy) UICountingLabelFormatBlock formatBlock; +@property (nonatomic, copy) UICountingLabelAttributedFormatBlock attributedFormatBlock; +@property (nonatomic, copy) void (^completionBlock)(); + +-(void)countFrom:(CGFloat)startValue to:(CGFloat)endValue; +-(void)countFrom:(CGFloat)startValue to:(CGFloat)endValue withDuration:(NSTimeInterval)duration; + +-(void)countFromCurrentValueTo:(CGFloat)endValue; +-(void)countFromCurrentValueTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration; + +-(void)countFromZeroTo:(CGFloat)endValue; +-(void)countFromZeroTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration; + +- (CGFloat)currentValue; + +@end + diff --git a/PNChartdemo/PNChartdemo/UICountingLabel/UICountingLabel.m b/PNChartdemo/PNChartdemo/UICountingLabel/UICountingLabel.m new file mode 100755 index 0000000..3a75a50 --- /dev/null +++ b/PNChartdemo/PNChartdemo/UICountingLabel/UICountingLabel.m @@ -0,0 +1,234 @@ +#import <QuartzCore/QuartzCore.h> + +#import "UICountingLabel.h" + +#if !__has_feature(objc_arc) +#error UICountingLabel is ARC only. Either turn on ARC for the project or use -fobjc-arc flag +#endif + +#pragma mark - UILabelCounter + +#ifndef kUILabelCounterRate +#define kUILabelCounterRate 3.0 +#endif + +@protocol UILabelCounter<NSObject> + +-(CGFloat)update:(CGFloat)t; + +@end + +@interface UILabelCounterLinear : NSObject<UILabelCounter> + +@end + +@interface UILabelCounterEaseIn : NSObject<UILabelCounter> + +@end + +@interface UILabelCounterEaseOut : NSObject<UILabelCounter> + +@end + +@interface UILabelCounterEaseInOut : NSObject<UILabelCounter> + +@end + +@implementation UILabelCounterLinear + +-(CGFloat)update:(CGFloat)t +{ + return t; +} + +@end + +@implementation UILabelCounterEaseIn + +-(CGFloat)update:(CGFloat)t +{ + return powf(t, kUILabelCounterRate); +} + +@end + +@implementation UILabelCounterEaseOut + +-(CGFloat)update:(CGFloat)t{ + return 1.0-powf((1.0-t), kUILabelCounterRate); +} + +@end + +@implementation UILabelCounterEaseInOut + +-(CGFloat) update: (CGFloat) t +{ + t *= 2; + if (t < 1) + return 0.5f * powf (t, kUILabelCounterRate); + else + return 0.5f * (2.0f - powf(2.0 - t, kUILabelCounterRate)); +} + +@end + +#pragma mark - UICountingLabel + +@interface UICountingLabel () + +@property CGFloat startingValue; +@property CGFloat destinationValue; +@property NSTimeInterval progress; +@property NSTimeInterval lastUpdate; +@property NSTimeInterval totalTime; +@property CGFloat easingRate; + +@property (nonatomic, strong) CADisplayLink *timer; +@property (nonatomic, strong) id<UILabelCounter> counter; + +@end + +@implementation UICountingLabel + +-(void)countFrom:(CGFloat)value to:(CGFloat)endValue { + + if (self.animationDuration == 0.0f) { + self.animationDuration = 2.0f; + } + + [self countFrom:value to:endValue withDuration:self.animationDuration]; +} + +-(void)countFrom:(CGFloat)startValue to:(CGFloat)endValue withDuration:(NSTimeInterval)duration { + + self.startingValue = startValue; + self.destinationValue = endValue; + + // remove any (possible) old timers + [self.timer invalidate]; + self.timer = nil; + + if (duration == 0.0) { + // No animation + [self setTextValue:endValue]; + [self runCompletionBlock]; + return; + } + + self.easingRate = 3.0f; + self.progress = 0; + self.totalTime = duration; + self.lastUpdate = [NSDate timeIntervalSinceReferenceDate]; + + if(self.format == nil) + self.format = @"%f"; + + switch(self.method) + { + case UILabelCountingMethodLinear: + self.counter = [[UILabelCounterLinear alloc] init]; + break; + case UILabelCountingMethodEaseIn: + self.counter = [[UILabelCounterEaseIn alloc] init]; + break; + case UILabelCountingMethodEaseOut: + self.counter = [[UILabelCounterEaseOut alloc] init]; + break; + case UILabelCountingMethodEaseInOut: + self.counter = [[UILabelCounterEaseInOut alloc] init]; + break; + } + + CADisplayLink *timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateValue:)]; + timer.frameInterval = 2; + [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode]; + self.timer = timer; +} + +- (void)countFromCurrentValueTo:(CGFloat)endValue { + [self countFrom:[self currentValue] to:endValue]; +} + +- (void)countFromCurrentValueTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration { + [self countFrom:[self currentValue] to:endValue withDuration:duration]; +} + +- (void)countFromZeroTo:(CGFloat)endValue { + [self countFrom:0.0f to:endValue]; +} + +- (void)countFromZeroTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration { + [self countFrom:0.0f to:endValue withDuration:duration]; +} + +- (void)updateValue:(NSTimer *)timer { + + // update progress + NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; + self.progress += now - self.lastUpdate; + self.lastUpdate = now; + + if (self.progress >= self.totalTime) { + [self.timer invalidate]; + self.timer = nil; + self.progress = self.totalTime; + } + + [self setTextValue:[self currentValue]]; + + if (self.progress == self.totalTime) { + [self runCompletionBlock]; + } +} + +- (void)setTextValue:(CGFloat)value +{ + if (self.attributedFormatBlock != nil) { + self.attributedText = self.attributedFormatBlock(value); + } + else if(self.formatBlock != nil) + { + self.text = self.formatBlock(value); + } + else + { + // check if counting with ints - cast to int + if([self.format rangeOfString:@"%(.*)d" options:NSRegularExpressionSearch].location != NSNotFound || [self.format rangeOfString:@"%(.*)i"].location != NSNotFound ) + { + self.text = [NSString stringWithFormat:self.format,(int)value]; + } + else + { + self.text = [NSString stringWithFormat:self.format,value]; + } + } +} + +- (void)setFormat:(NSString *)format { + _format = format; + // update label with new format + [self setTextValue:self.currentValue]; +} + +- (void)runCompletionBlock { + + if (self.completionBlock) { + self.completionBlock(); + self.completionBlock = nil; + } +} + +- (CGFloat)currentValue { + + if (self.progress >= self.totalTime) { + return self.destinationValue; + } + + CGFloat percent = self.progress / self.totalTime; + CGFloat updateVal = [self.counter update:percent]; + return self.startingValue + (updateVal * (self.destinationValue - self.startingValue)); +} + +@end diff --git a/camerademo/camerademo/ViewController.h b/PNChartdemo/PNChartdemo/ViewController.h similarity index 75% rename from camerademo/camerademo/ViewController.h rename to PNChartdemo/PNChartdemo/ViewController.h index ca96e2d..fbaa548 100644 --- a/camerademo/camerademo/ViewController.h +++ b/PNChartdemo/PNChartdemo/ViewController.h @@ -1,8 +1,8 @@ // // ViewController.h -// camerademo +// PNChartdemo // -// Created by WindShan on 2017/2/21. +// Created by WindShan on 2017/3/3. // Copyright �� 2017��� WindShan. All rights reserved. // diff --git a/camerademo/camerademo/ViewController.m b/PNChartdemo/PNChartdemo/ViewController.m similarity index 89% rename from camerademo/camerademo/ViewController.m rename to PNChartdemo/PNChartdemo/ViewController.m index d49c4e7..93fbcb3 100644 --- a/camerademo/camerademo/ViewController.m +++ b/PNChartdemo/PNChartdemo/ViewController.m @@ -1,8 +1,8 @@ // // ViewController.m -// camerademo +// PNChartdemo // -// Created by WindShan on 2017/2/21. +// Created by WindShan on 2017/3/3. // Copyright �� 2017��� WindShan. All rights reserved. // diff --git a/camerademo/camerademo/main.m b/PNChartdemo/PNChartdemo/main.m similarity index 83% rename from camerademo/camerademo/main.m rename to PNChartdemo/PNChartdemo/main.m index 27a8a02..c92139f 100644 --- a/camerademo/camerademo/main.m +++ b/PNChartdemo/PNChartdemo/main.m @@ -1,8 +1,8 @@ // // main.m -// camerademo +// PNChartdemo // -// Created by WindShan on 2017/2/21. +// Created by WindShan on 2017/3/3. // Copyright �� 2017��� WindShan. All rights reserved. // diff --git a/camerademo/camerademo.xcodeproj/project.pbxproj b/camerademo/camerademo.xcodeproj/project.pbxproj deleted file mode 100644 index a75fd57..0000000 --- a/camerademo/camerademo.xcodeproj/project.pbxproj +++ /dev/null @@ -1,317 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - E52F59F01E5C21890084B3E2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E52F59EF1E5C21890084B3E2 /* main.m */; }; - E52F59F31E5C21890084B3E2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E52F59F21E5C21890084B3E2 /* AppDelegate.m */; }; - E52F59F61E5C21890084B3E2 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E52F59F51E5C21890084B3E2 /* ViewController.m */; }; - E52F59F91E5C21890084B3E2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E52F59F71E5C21890084B3E2 /* Main.storyboard */; }; - E52F59FB1E5C21890084B3E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E52F59FA1E5C21890084B3E2 /* Assets.xcassets */; }; - E52F59FE1E5C21890084B3E2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E52F59FC1E5C21890084B3E2 /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - E52F59EB1E5C21890084B3E2 /* camerademo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = camerademo.app; sourceTree = BUILT_PRODUCTS_DIR; }; - E52F59EF1E5C21890084B3E2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; }; - E52F59F11E5C21890084B3E2 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; - E52F59F21E5C21890084B3E2 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; - E52F59F41E5C21890084B3E2 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; }; - E52F59F51E5C21890084B3E2 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; }; - E52F59F81E5C21890084B3E2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; - E52F59FA1E5C21890084B3E2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; - E52F59FD1E5C21890084B3E2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; - E52F59FF1E5C21890084B3E2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - E52F59E81E5C21890084B3E2 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - E52F59E21E5C21890084B3E2 = { - isa = PBXGroup; - children = ( - E52F59ED1E5C21890084B3E2 /* camerademo */, - E52F59EC1E5C21890084B3E2 /* Products */, - ); - sourceTree = "<group>"; - }; - E52F59EC1E5C21890084B3E2 /* Products */ = { - isa = PBXGroup; - children = ( - E52F59EB1E5C21890084B3E2 /* camerademo.app */, - ); - name = Products; - sourceTree = "<group>"; - }; - E52F59ED1E5C21890084B3E2 /* camerademo */ = { - isa = PBXGroup; - children = ( - E52F59F11E5C21890084B3E2 /* AppDelegate.h */, - E52F59F21E5C21890084B3E2 /* AppDelegate.m */, - E52F59F41E5C21890084B3E2 /* ViewController.h */, - E52F59F51E5C21890084B3E2 /* ViewController.m */, - E52F59F71E5C21890084B3E2 /* Main.storyboard */, - E52F59FA1E5C21890084B3E2 /* Assets.xcassets */, - E52F59FC1E5C21890084B3E2 /* LaunchScreen.storyboard */, - E52F59FF1E5C21890084B3E2 /* Info.plist */, - E52F59EE1E5C21890084B3E2 /* Supporting Files */, - ); - path = camerademo; - sourceTree = "<group>"; - }; - E52F59EE1E5C21890084B3E2 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - E52F59EF1E5C21890084B3E2 /* main.m */, - ); - name = "Supporting Files"; - sourceTree = "<group>"; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - E52F59EA1E5C21890084B3E2 /* camerademo */ = { - isa = PBXNativeTarget; - buildConfigurationList = E52F5A021E5C21890084B3E2 /* Build configuration list for PBXNativeTarget "camerademo" */; - buildPhases = ( - E52F59E71E5C21890084B3E2 /* Sources */, - E52F59E81E5C21890084B3E2 /* Frameworks */, - E52F59E91E5C21890084B3E2 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = camerademo; - productName = camerademo; - productReference = E52F59EB1E5C21890084B3E2 /* camerademo.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - E52F59E31E5C21890084B3E2 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0820; - ORGANIZATIONNAME = WindShan; - TargetAttributes = { - E52F59EA1E5C21890084B3E2 = { - CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = VGXA77XL6T; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = E52F59E61E5C21890084B3E2 /* Build configuration list for PBXProject "camerademo" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = E52F59E21E5C21890084B3E2; - productRefGroup = E52F59EC1E5C21890084B3E2 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - E52F59EA1E5C21890084B3E2 /* camerademo */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - E52F59E91E5C21890084B3E2 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - E52F59FE1E5C21890084B3E2 /* LaunchScreen.storyboard in Resources */, - E52F59FB1E5C21890084B3E2 /* Assets.xcassets in Resources */, - E52F59F91E5C21890084B3E2 /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - E52F59E71E5C21890084B3E2 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - E52F59F61E5C21890084B3E2 /* ViewController.m in Sources */, - E52F59F31E5C21890084B3E2 /* AppDelegate.m in Sources */, - E52F59F01E5C21890084B3E2 /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - E52F59F71E5C21890084B3E2 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - E52F59F81E5C21890084B3E2 /* Base */, - ); - name = Main.storyboard; - sourceTree = "<group>"; - }; - E52F59FC1E5C21890084B3E2 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - E52F59FD1E5C21890084B3E2 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = "<group>"; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - E52F5A001E5C21890084B3E2 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - E52F5A011E5C21890084B3E2 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.2; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - E52F5A031E5C21890084B3E2 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = VGXA77XL6T; - INFOPLIST_FILE = camerademo/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.moral.camerademo.camerademo; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - E52F5A041E5C21890084B3E2 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = VGXA77XL6T; - INFOPLIST_FILE = camerademo/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.moral.camerademo.camerademo; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - E52F59E61E5C21890084B3E2 /* Build configuration list for PBXProject "camerademo" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - E52F5A001E5C21890084B3E2 /* Debug */, - E52F5A011E5C21890084B3E2 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - E52F5A021E5C21890084B3E2 /* Build configuration list for PBXNativeTarget "camerademo" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - E52F5A031E5C21890084B3E2 /* Debug */, - E52F5A041E5C21890084B3E2 /* Release */, - ); - defaultConfigurationIsVisible = 0; - }; -/* End XCConfigurationList section */ - }; - rootObject = E52F59E31E5C21890084B3E2 /* Project object */; -} diff --git a/camerademo/camerademo.xcodeproj/project.xcworkspace/xcuserdata/WindShan.xcuserdatad/UserInterfaceState.xcuserstate b/camerademo/camerademo.xcodeproj/project.xcworkspace/xcuserdata/WindShan.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 3f97b50..0000000 --- a/camerademo/camerademo.xcodeproj/project.xcworkspace/xcuserdata/WindShan.xcuserdatad/UserInterfaceState.xcuserstate +++ /dev/null Binary files differ diff --git a/camerademo/camerademo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/camerademo.xcscheme b/camerademo/camerademo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/camerademo.xcscheme deleted file mode 100644 index b422b1d..0000000 --- a/camerademo/camerademo.xcodeproj/xcuserdata/WindShan.xcuserdatad/xcschemes/camerademo.xcscheme +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<Scheme - LastUpgradeVersion = "0820" - version = "1.3"> - <BuildAction - parallelizeBuildables = "YES" - buildImplicitDependencies = "YES"> - </BuildAction> - <TestAction - buildConfiguration = "Debug" - selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" - selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> - <Testables> - </Testables> - <AdditionalOptions> - </AdditionalOptions> - </TestAction> - <LaunchAction - buildConfiguration = "Debug" - selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" - selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - launchStyle = "0" - useCustomWorkingDirectory = "NO" - ignoresPersistentStateOnLaunch = "NO" - debugDocumentVersioning = "YES" - debugServiceExtension = "internal" - allowLocationSimulation = "YES"> - <AdditionalOptions> - </AdditionalOptions> - </LaunchAction> - <ProfileAction - buildConfiguration = "Release" - shouldUseLaunchSchemeArgsEnv = "YES" - savedToolIdentifier = "" - useCustomWorkingDirectory = "NO" - debugDocumentVersioning = "YES"> - </ProfileAction> - <AnalyzeAction - buildConfiguration = "Debug"> - </AnalyzeAction> - <ArchiveAction - buildConfiguration = "Release" - revealArchiveInOrganizer = "YES"> - </ArchiveAction> -</Scheme> diff --git a/camerademo/camerademo/Base.lproj/Main.storyboard b/camerademo/camerademo/Base.lproj/Main.storyboard deleted file mode 100644 index 4529698..0000000 --- a/camerademo/camerademo/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> - <dependencies> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/> - <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> - </dependencies> - <scenes> - <!--View Controller--> - <scene sceneID="tne-QT-ifu"> - <objects> - <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController"> - <layoutGuides> - <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> - <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> - </layoutGuides> - <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> - <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - </view> - </viewController> - <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> - </objects> - </scene> - </scenes> -</document> diff --git a/camerademo/camerademo/demo/FileManager/MPFileManager.h b/camerademo/camerademo/demo/FileManager/MPFileManager.h deleted file mode 100755 index 15562d4..0000000 --- a/camerademo/camerademo/demo/FileManager/MPFileManager.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// MPFileManager.h -// MobileProject ��������������� -// -// Created by wujunyang on 16/7/22. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import <Foundation/Foundation.h> -#import <AssetsLibrary/ALAsset.h> -#import <AssetsLibrary/ALAssetsLibrary.h> -#import <AssetsLibrary/ALAssetsGroup.h> -#import <AssetsLibrary/ALAssetRepresentation.h> - -@interface MPFileManager : NSObject - -+ (MPFileManager *)sharedManager; - -//������������������ -+ (NSString *)downloadPath; -//������������������ -+ (NSString *)uploadPath; - -//���������������������APP������������ -+ (BOOL)writeUploadDataWithName:(NSString *)fileName andAsset:(ALAsset *)asset; -//���������������������APP������������ -+ (BOOL)writeUploadDataWithName:(NSString *)fileName andImage:(UIImage *)image; -//������APP��������������������� -+ (BOOL)deleteUploadDataWithName:(NSString *)fileName; - -@end diff --git a/camerademo/camerademo/demo/FileManager/MPFileManager.m b/camerademo/camerademo/demo/FileManager/MPFileManager.m deleted file mode 100755 index f514428..0000000 --- a/camerademo/camerademo/demo/FileManager/MPFileManager.m +++ /dev/null @@ -1,133 +0,0 @@ -// -// MPFileManager.m -// MobileProject -// -// Created by wujunyang on 16/7/22. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import "MPFileManager.h" - -@implementation MPFileManager - -+ (MPFileManager *)sharedManager { - static MPFileManager *_sharedManager = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _sharedManager = [[MPFileManager alloc] init]; - }); - return _sharedManager; -} - -- (instancetype)init -{ - self = [super init]; - if (self) { - [[self class] createFolder:[[self class] downloadPath]]; - [[self class] createFolder:[[self class] uploadPath]]; - } - return self; -} - -/** - * @author wujunyang, 16-07-22 11:07:41 - * - * @brief ������������������ - * - * @return <#return value description#> - */ -+ (NSString *)downloadPath{ - NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; - NSString *downloadPath = [documentPath stringByAppendingPathComponent:@"MobileProject_Download"]; - return downloadPath; -} - -/** - * @author wujunyang, 16-07-22 11:07:58 - * - * @brief ������������������ - * - * @return <#return value description#> - */ -+ (NSString *)uploadPath{ - NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; - NSString *uploadPath = [documentPath stringByAppendingPathComponent:@"MobileProject_Upload"]; - return uploadPath; -} - -/** - * @author wujunyang, 16-07-22 11:07:50 - * - * @brief ��������������������� - * - * @param path <#path description#> - * - * @return <#return value description#> - */ -+ (BOOL)createFolder:(NSString *)path{ - BOOL isDir = NO; - NSFileManager *fileManager = [NSFileManager defaultManager]; - BOOL existed = [fileManager fileExistsAtPath:path isDirectory:&isDir]; - BOOL isCreated = NO; - if (!(isDir == YES && existed == YES)){ - isCreated = [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; - }else{ - isCreated = YES; - } - return isCreated; -} - - -+ (BOOL)writeUploadDataWithName:(NSString *)fileName andAsset:(ALAsset *)asset{ - if (![self createFolder:[self uploadPath]]) { - return NO; - } - NSString *filePath = [[self uploadPath] stringByAppendingPathComponent:fileName]; - - [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]; - NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:filePath]; - if (!handle) { - return NO; - } - static const NSUInteger BufferSize = 1024*1024; - - ALAssetRepresentation *rep = [asset defaultRepresentation]; - uint8_t *buffer = calloc(BufferSize, sizeof(*buffer)); - NSUInteger offset = 0, bytesRead = 0; - - do { - @try { - bytesRead = [rep getBytes:buffer fromOffset:offset length:BufferSize error:nil]; - [handle writeData:[NSData dataWithBytesNoCopy:buffer length:bytesRead freeWhenDone:NO]]; - offset += bytesRead; - } @catch (NSException *exception) { - free(buffer); - - return NO; - } - } while (bytesRead > 0); - - free(buffer); - return YES; -} - -+ (BOOL)writeUploadDataWithName:(NSString *)fileName andImage:(UIImage *)image{ - if (![self createFolder:[self uploadPath]]) { - return NO; - } - NSString *filePath = [[self uploadPath] stringByAppendingPathComponent:fileName]; - - return [UIImageJPEGRepresentation(image, 1.0) writeToFile:filePath options:NSAtomicWrite error:nil]; -} - -+ (BOOL)deleteUploadDataWithName:(NSString *)fileName{ - NSString *filePath = [[self uploadPath] stringByAppendingPathComponent:fileName]; - NSFileManager *fm = [NSFileManager defaultManager]; - if ([fm fileExistsAtPath:filePath]) { - return [fm removeItemAtPath:filePath error:nil]; - }else{ - return YES; - } -} - -@end diff --git a/camerademo/camerademo/demo/MPUploadImageHelper.h b/camerademo/camerademo/demo/MPUploadImageHelper.h deleted file mode 100755 index e752fb2..0000000 --- a/camerademo/camerademo/demo/MPUploadImageHelper.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// MPUploadImageHelper.h -// MobileProject -// -// Created by wujunyang on 16/7/20. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import <Foundation/Foundation.h> -#import "MPImageItemModel.h" - -@interface MPUploadImageHelper : NSObject - -//������������������imagesArray ���������selectedAssetURLs������������KVO������������ -@property (readwrite, nonatomic, strong) NSMutableArray *imagesArray; -@property (readwrite, nonatomic, strong) NSMutableArray *selectedAssetURLs; - - -- (void)addASelectedAssetURL:(NSURL *)assetURL; -- (void)deleteASelectedAssetURL:(NSURL *)assetURL; -- (void)deleteAImage:(MPImageItemModel *)imageInfo; - -/** - * @author wujunyang, 16-07-25 13:07:17 - * - * @brief <#Description#> - * - * @param isUploadProcess ������������������������YES��������������������� - * - * @return <#return value description#> - */ -+(MPUploadImageHelper *)MPUploadImageForSend:(BOOL)isUploadProcess; - -@end diff --git a/camerademo/camerademo/demo/MPUploadImageHelper.m b/camerademo/camerademo/demo/MPUploadImageHelper.m deleted file mode 100755 index 8cd4d65..0000000 --- a/camerademo/camerademo/demo/MPUploadImageHelper.m +++ /dev/null @@ -1,82 +0,0 @@ -// -// MPUploadImageHelper.m -// MobileProject -// -// Created by wujunyang on 16/7/20. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import "MPUploadImageHelper.h" - -@interface MPUploadImageHelper() -@property(nonatomic)BOOL isUploadProcess; -@end - -static MPUploadImageHelper *_mpUploadImageHelper = nil; - -@implementation MPUploadImageHelper - -+(MPUploadImageHelper *)MPUploadImageForSend:(BOOL)isUploadProcess -{ - _mpUploadImageHelper = [[MPUploadImageHelper alloc] init]; - _mpUploadImageHelper.isUploadProcess=isUploadProcess; - return _mpUploadImageHelper; -} - -- (void)setSelectedAssetURLs:(NSMutableArray *)selectedAssetURLs{ - NSMutableArray *needToAdd = [NSMutableArray new]; - NSMutableArray *needToDelete = [NSMutableArray new]; - [self.selectedAssetURLs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - if (![selectedAssetURLs containsObject:obj]) { - [needToDelete addObject:obj]; - } - }]; - [needToDelete enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - [self deleteASelectedAssetURL:obj]; - }]; - [selectedAssetURLs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - if (![self.selectedAssetURLs containsObject:obj]) { - [needToAdd addObject:obj]; - } - }]; - [needToAdd enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - [self addASelectedAssetURL:obj]; - }]; -} - - -- (void)addASelectedAssetURL:(NSURL *)assetURL{ - if (!_selectedAssetURLs) { - _selectedAssetURLs = [NSMutableArray new]; - } - if (!_imagesArray) { - _imagesArray = [NSMutableArray new]; - } - - [_selectedAssetURLs addObject:assetURL]; - - NSMutableArray *imagesArray = [self mutableArrayValueForKey:@"imagesArray"];//������kvo - MPImageItemModel *imageItem = [MPImageItemModel imageWithAssetURL:assetURL isUploadProcess:self.isUploadProcess]; - [imagesArray addObject:imageItem]; -} - -- (void)deleteASelectedAssetURL:(NSURL *)assetURL{ - [self.selectedAssetURLs removeObject:assetURL]; - NSMutableArray *imagesArray = [self mutableArrayValueForKey:@"imagesArray"];//������kvo - [imagesArray enumerateObjectsUsingBlock:^(MPImageItemModel *obj, NSUInteger idx, BOOL *stop) { - if (obj.assetURL == assetURL) { - [imagesArray removeObject:obj]; - *stop = YES; - } - }]; -} - -- (void)deleteAImage:(MPImageItemModel *)imageInfo{ - NSMutableArray *imagesArray = [self mutableArrayValueForKey:@"imagesArray"];//������kvo - [imagesArray removeObject:imageInfo]; - if (imageInfo.assetURL) { - [self.selectedAssetURLs removeObject:imageInfo.assetURL]; - } -} - -@end diff --git a/camerademo/camerademo/demo/MPUploadImagesViewController.h b/camerademo/camerademo/demo/MPUploadImagesViewController.h deleted file mode 100755 index 1ed9601..0000000 --- a/camerademo/camerademo/demo/MPUploadImagesViewController.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// MPUploadImagesViewController.h -// MobileProject ������������ -// -// Created by wujunyang on 16/7/20. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import <UIKit/UIKit.h> - - -@interface MPUploadImagesViewController : UIViewController - -@end diff --git a/camerademo/camerademo/demo/MPUploadImagesViewController.m b/camerademo/camerademo/demo/MPUploadImagesViewController.m deleted file mode 100755 index 99c2a66..0000000 --- a/camerademo/camerademo/demo/MPUploadImagesViewController.m +++ /dev/null @@ -1,231 +0,0 @@ -// -// MPUploadImagesViewController.m -// MobileProject -// -// Created by wujunyang on 16/7/20. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import "MPUploadImagesViewController.h" - -@interface MPUploadImagesViewController()<UITableViewDataSource, UITableViewDelegate,UIActionSheetDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate, QBImagePickerControllerDelegate> -@property (nonatomic,strong) UITableView *myTableView; -@property (strong, nonatomic) MPUploadImageHelper *curUploadImageHelper; -@end - - -@implementation MPUploadImagesViewController - - -- (void)viewDidLoad { - [super viewDidLoad]; - self.view.backgroundColor=[UIColor whiteColor]; - self.navigationItem.title=@"������������"; - - //��������� - _curUploadImageHelper=[MPUploadImageHelper MPUploadImageForSend:NO]; - - //��������������� - if (!_myTableView) { - _myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0,0, Main_Screen_Width, Main_Screen_Height) style:UITableViewStylePlain]; - _myTableView.tableFooterView=[UIView new]; - _myTableView.showsVerticalScrollIndicator = NO; - _myTableView.showsHorizontalScrollIndicator = NO; - _myTableView.dataSource = self; - _myTableView.delegate = self; - [_myTableView registerClass:[MPImageUploadCell class] forCellReuseIdentifier:NSStringFromClass([MPImageUploadCell class])]; - [self.view addSubview:_myTableView]; - [_myTableView mas_makeConstraints:^(MASConstraintMaker *make) { - make.edges.equalTo(UIEdgeInsetsMake(0, 0, 0, 0)); - }]; - } - - //������������ - UIButton*rightButton = [[UIButton alloc]initWithFrame:CGRectMake(0,0,70,30)]; - [rightButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; - [rightButton setTitle:@"������" forState:UIControlStateNormal]; - [rightButton addTarget:self action:@selector(myAction)forControlEvents:UIControlEventTouchUpInside]; - UIBarButtonItem*rightItem = [[UIBarButtonItem alloc]initWithCustomView:rightButton]; - self.navigationItem.rightBarButtonItem= rightItem; -} - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; -} - - -- (void)dealloc -{ - _myTableView.delegate = nil; - _myTableView.dataSource = nil; -} - -#pragma mark UITableViewDataSource, UITableViewDelegate������������ - --(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return 1; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ - return 1; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - MPImageUploadCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([MPImageUploadCell class]) forIndexPath:indexPath]; - __weak typeof(self)weakSelf = self; - cell.accessoryType = UITableViewCellAccessoryNone; - cell.curUploadImageHelper=self.curUploadImageHelper; - cell.addPicturesBlock = ^(){ - [weakSelf showActionForPhoto]; - }; - cell.deleteImageBlock = ^(MPImageItemModel *toDelete) - { - [weakSelf.curUploadImageHelper deleteAImage:toDelete]; - [weakSelf.myTableView reloadData]; - }; - return cell; -} - -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ - return [MPImageUploadCell cellHeightWithObj:self.curUploadImageHelper]; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ - [tableView deselectRowAtIndexPath:indexPath animated:YES]; -} - - -#pragma mark UIActionSheetDelegate - -- (void)actionSheet:(UIActionSheet *)modalView clickedButtonAtIndex:(NSInteger)buttonIndex -{ - if (buttonIndex == 0) - { - //������ - if (![cameraHelper checkCameraAuthorizationStatus]) { - return; - } - UIImagePickerController *picker = [[UIImagePickerController alloc] init]; - picker.delegate = self; - picker.allowsEditing = NO;//��������������� - picker.sourceType = UIImagePickerControllerSourceTypeCamera; - [self presentViewController:picker animated:YES completion:nil];//������������������ - }else if (buttonIndex == 1) - { - //������ - if (![cameraHelper checkPhotoLibraryAuthorizationStatus]) { - return; - } - QBImagePickerController *imagePickerController = [[QBImagePickerController alloc] init]; - [imagePickerController.selectedAssetURLs removeAllObjects]; - [imagePickerController.selectedAssetURLs addObjectsFromArray:self.curUploadImageHelper.selectedAssetURLs]; - imagePickerController.filterType = QBImagePickerControllerFilterTypePhotos; - imagePickerController.delegate = self; - imagePickerController.maximumNumberOfSelection = kupdateMaximumNumberOfImage; - imagePickerController.allowsMultipleSelection = YES; - UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:imagePickerController]; - [self presentViewController:navigationController animated:YES completion:NULL]; - } -} - - -#pragma mark UIImagePickerControllerDelegate - -- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info -{ - UIImage *pickerImage = [info objectForKey:UIImagePickerControllerOriginalImage]; - ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init]; - [assetsLibrary writeImageToSavedPhotosAlbum:[pickerImage CGImage] orientation:(ALAssetOrientation)pickerImage.imageOrientation completionBlock:^(NSURL *assetURL, NSError *error) { - [self.curUploadImageHelper addASelectedAssetURL:assetURL]; - //������������ ������������������������ - [self partialTableViewRefresh]; - }]; - [picker dismissViewControllerAnimated:YES completion:^{}]; -} - -- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker -{ - [picker dismissViewControllerAnimated:YES completion:nil]; -} - - -#pragma mark UINavigationControllerDelegate, QBImagePickerControllerDelegate - -- (void)qb_imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets{ - NSMutableArray *selectedAssetURLs = [NSMutableArray new]; - [imagePickerController.selectedAssetURLs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - [selectedAssetURLs addObject:obj]; - }]; - MPWeakSelf(self) - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - self.curUploadImageHelper.selectedAssetURLs = selectedAssetURLs; - dispatch_async(dispatch_get_main_queue(), ^{ - MPStrongSelf(self) - //������������ ������������������������ - [self partialTableViewRefresh]; - }); - }); - [self dismissViewControllerAnimated:YES completion:nil]; -} -- (void)qb_imagePickerControllerDidCancel:(QBImagePickerController *)imagePickerController{ - [self dismissViewControllerAnimated:YES completion:nil]; -} - -#pragma mark ��������������� - -//��������������� --(void)showActionForPhoto -{ - UIActionSheet *actionSheet = [[UIActionSheet alloc] - initWithTitle:nil - delegate:self - cancelButtonTitle:@"������" - destructiveButtonTitle:nil - otherButtonTitles:@"������",@"���������������",nil]; - actionSheet.actionSheetStyle = UIActionSheetStyleBlackOpaque; - [actionSheet showInView:self.view]; -} - -//��������������������������������� ������������������������ --(void)partialTableViewRefresh -{ - [self.myTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationFade]; -} - -//������������ --(void)myAction -{ - if (self.curUploadImageHelper.selectedAssetURLs.count==0) { - [MBProgressHUD showAutoMessage:@"���������������������������" ToView:nil]; - return; - } - - MPUploadImageService *req=[[MPUploadImageService alloc]initWithUploadImages:self.curUploadImageHelper]; - - [req startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest *request) { - //������������������������������ - - } failure:^(__kindof YTKBaseRequest *request) { - [MPRequstFailedHelper requstFailed:request]; - }]; - - //������������ - MPWeakSelf(self) - req.uploadPropressBlock = ^(NSUInteger __unused bytesWritten, - long long totalBytesWritten, - long long totalBytesExpectedToWrite) - { - - MPStrongSelf(self); - CGFloat propress = totalBytesWritten*1.0/totalBytesExpectedToWrite; - NSLog(@"���������������%lld/%lld___%2f",totalBytesWritten,totalBytesExpectedToWrite,propress); - dispatch_async(dispatch_get_main_queue(), ^{ - //������UI - }); - - - }; -} -@end diff --git a/camerademo/camerademo/demo/Model/MPImageItemModel.h b/camerademo/camerademo/demo/Model/MPImageItemModel.h deleted file mode 100755 index a23da37..0000000 --- a/camerademo/camerademo/demo/Model/MPImageItemModel.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// MPImageItemModel.h -// MobileProject ������������������������ -// -// Created by wujunyang on 16/7/20. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import <Foundation/Foundation.h> -#import "MPFileManager.h" -#import "imageCompressHelper.h" -#import "dateTimeHelper.h" -#import "UIImage+Resize.h" -#import "UIImage+FX.h" - -typedef NS_ENUM(NSInteger, MPImageUploadState) -{ - MPImageUploadStateInit = 0, - MPImageUploadStateIng, - MPImageUploadStateSuccess, - MPImageUploadStateFail -}; - - -@interface MPImageItemModel : NSObject - -//������ ��������� -@property (readwrite, nonatomic, strong) UIImage *image, *thumbnailImage; -//������������asstURL -@property (strong, nonatomic) NSURL *assetURL; -//������������ -@property (assign, nonatomic) MPImageUploadState uploadState; -//���������������������[���������������������������������������������������������������������������������������������������,������������] -@property(nonatomic,copy)NSString *httpUrl; -@property(nonatomic,copy)NSString *upServicePath; -//������������������ ������������ ��������� ������������ -@property (readwrite, nonatomic, strong) NSString *photoName; -@property (readwrite, nonatomic, strong) NSString *photoLatitude; -@property (readwrite, nonatomic, strong) NSString *photoLongitude; -@property (readwrite, nonatomic, strong) NSString *photoTime; - -//������������ -+ (instancetype)imageWithAssetURL:(NSURL *)assetURL isUploadProcess:(BOOL)isUploadProcess; -+ (instancetype)imageWithAssetURL:(NSURL *)assetURL andImage:(UIImage *)image; - -@end diff --git a/camerademo/camerademo/demo/Model/MPImageItemModel.m b/camerademo/camerademo/demo/Model/MPImageItemModel.m deleted file mode 100755 index 2d0bc80..0000000 --- a/camerademo/camerademo/demo/Model/MPImageItemModel.m +++ /dev/null @@ -1,100 +0,0 @@ -// -// MPImageItemModel.m -// MobileProject -// -// Created by wujunyang on 16/7/20. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import "MPImageItemModel.h" -#import <CoreLocation/CoreLocation.h> - -@implementation MPImageItemModel - -+ (instancetype)imageWithAssetURL:(NSURL *)assetURL isUploadProcess:(BOOL)isUploadProcess{ - MPImageItemModel *imageItem = [[MPImageItemModel alloc] init]; - imageItem.uploadState = MPImageUploadStateInit; - imageItem.assetURL = assetURL; - - MPWeakSelf(self); - - void (^selectAsset)(ALAsset *) = ^(ALAsset *asset){ - if (asset) { - UIImage *highQualityImage = [imageCompressHelper fullScreenImageALAsset:asset]; - UIImage *thumbnailImage = [UIImage imageWithCGImage:[asset thumbnail]]; - - //������������ - MPStrongSelf(self); - [self imageInfoWithALAsset:asset imageItem:imageItem]; - - dispatch_async(dispatch_get_main_queue(), ^{ - imageItem.image = [imageCompressHelper compressedImageToLimitSizeOfKB:100 image:highQualityImage];; - imageItem.thumbnailImage = thumbnailImage; - - if (isUploadProcess) { - //��������������� ��������������������������������� - [MPFileManager writeUploadDataWithName:imageItem.photoName andImage:imageItem.image]; - } - }); - } - }; - - ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init]; - MPWeakSelf(assetsLibrary); - [assetsLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) { - if (asset) { - selectAsset(asset); - }else{ - MPStrongSelf(assetsLibrary); - [assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupPhotoStream usingBlock:^(ALAssetsGroup *group, BOOL *stop) { - [group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stopG) { - if([result.defaultRepresentation.url isEqual:assetURL]) { - selectAsset(result); - *stop = YES; - *stopG = YES; - } - }]; - } failureBlock:^(NSError *error) { - NSLog(@"������������������"); - }]; - } - }failureBlock:^(NSError *error) { - NSLog(@"������������������"); - }]; - return imageItem; - -} - -//������������������������ -+(void)imageInfoWithALAsset:(ALAsset *)asset imageItem:(MPImageItemModel *)imageItem -{ - //������������ - NSDate * nsALAssetPropertyDate = [ asset valueForProperty:ALAssetPropertyDate ]; - if (nsALAssetPropertyDate!=nil) { - imageItem.photoTime=[dateTimeHelper htcTimeToLocationStr:nsALAssetPropertyDate]; - } - - //GPS������ - CLLocation *location = [asset valueForProperty:ALAssetPropertyLocation]; - if (location) { - CLLocationCoordinate2D curCoordinate=location.coordinate; - imageItem.photoLatitude=[NSString stringWithFormat:@"%f",curCoordinate.latitude]; - imageItem.photoLongitude=[NSString stringWithFormat:@"%f",curCoordinate.longitude]; - } - - //������������ - ALAssetRepresentation *representation = [asset defaultRepresentation]; - imageItem.photoName = [representation filename]; -} - - -+ (instancetype)imageWithAssetURL:(NSURL *)assetURL andImage:(UIImage *)image{ - MPImageItemModel *imageItem = [[MPImageItemModel alloc] init]; - imageItem.uploadState = MPImageUploadStateInit; - imageItem.assetURL = assetURL; - imageItem.image = image; - imageItem.thumbnailImage = [image imageScaledToSize:CGSizeMake(AdaptedWidth(70), AdaptedWidth(70))]; - return imageItem; -} - -@end diff --git a/camerademo/camerademo/demo/OtherHelper/UITapImageView.h b/camerademo/camerademo/demo/OtherHelper/UITapImageView.h deleted file mode 100755 index 3d3233b..0000000 --- a/camerademo/camerademo/demo/OtherHelper/UITapImageView.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// UITapImageView.h -// zxptUser -// -// Created by wujunyang on 16/6/8. -// Copyright �� 2016��� qijia. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface UITapImageView : UIImageView - -- (void)addTapBlock:(void(^)(id obj))tapAction; - --(void)setImageWithUrl:(NSURL *)imgUrl placeholderImage:(UIImage *)placeholderImage tapBlock:(void(^)(id obj))tapAction; - -@end diff --git a/camerademo/camerademo/demo/OtherHelper/UITapImageView.m b/camerademo/camerademo/demo/OtherHelper/UITapImageView.m deleted file mode 100755 index 4193a43..0000000 --- a/camerademo/camerademo/demo/OtherHelper/UITapImageView.m +++ /dev/null @@ -1,53 +0,0 @@ -// -// UITapImageView.m -// zxptUser -// -// Created by wujunyang on 16/6/8. -// Copyright �� 2016��� qijia. All rights reserved. -// - -#import "UITapImageView.h" - -@interface UITapImageView () - -@property (nonatomic, copy) void(^tapAction)(id); - -@end - -@implementation UITapImageView - -- (id)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - if (self) { - // Initialization code - } - return self; -} - -- (instancetype)init -{ - return [self initWithFrame:CGRectZero]; -} - -- (void)tap{ - if (self.tapAction) { - self.tapAction(self); - } -} -- (void)addTapBlock:(void(^)(id obj))tapAction{ - self.tapAction = tapAction; - if (![self gestureRecognizers]) { - self.userInteractionEnabled = YES; - UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap)]; - [self addGestureRecognizer:tap]; - } -} - --(void)setImageWithUrl:(NSURL *)imgUrl placeholderImage:(UIImage *)placeholderImage tapBlock:(void(^)(id obj))tapAction{ - [self sd_setImageWithURL:imgUrl placeholderImage:placeholderImage]; - [self addTapBlock:tapAction]; -} - - -@end diff --git a/camerademo/camerademo/demo/OtherHelper/cameraHelper.h b/camerademo/camerademo/demo/OtherHelper/cameraHelper.h deleted file mode 100755 index 609027f..0000000 --- a/camerademo/camerademo/demo/OtherHelper/cameraHelper.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// cameraHelper.h -// MobileProject -// -// Created by wujunyang on 16/7/20. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import <Foundation/Foundation.h> - -@interface cameraHelper : NSObject - -/** - * ������������"������"������������, ���������������������, ������������������������������������. - */ -+ (BOOL)checkPhotoLibraryAuthorizationStatus; - -/** - * ������������"������"������������, ���������������������, ������������������������������������. - */ -+ (BOOL)checkCameraAuthorizationStatus; - -@end diff --git a/camerademo/camerademo/demo/OtherHelper/cameraHelper.m b/camerademo/camerademo/demo/OtherHelper/cameraHelper.m deleted file mode 100755 index 73a9a94..0000000 --- a/camerademo/camerademo/demo/OtherHelper/cameraHelper.m +++ /dev/null @@ -1,50 +0,0 @@ -// -// cameraHelper.m -// MobileProject -// -// Created by wujunyang on 16/7/20. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import "cameraHelper.h" -#import <AssetsLibrary/ALAsset.h> -#import <AssetsLibrary/ALAssetsLibrary.h> -#import <AssetsLibrary/ALAssetsGroup.h> -#import <AssetsLibrary/ALAssetRepresentation.h> - -@import AVFoundation; - -@implementation cameraHelper - -+ (BOOL)checkPhotoLibraryAuthorizationStatus -{ - //if ([ALAssetsLibrary respondsToSelector:@selector(authorizationStatus)]) { - ALAuthorizationStatus authStatus = [ALAssetsLibrary authorizationStatus]; - if (ALAuthorizationStatusDenied == authStatus || - ALAuthorizationStatusRestricted == authStatus) { - [MBProgressHUD showAutoMessage:@"������iPhone������������->������->������������������������������������������" ToView:nil]; - return NO; - // } - } - return YES; -} - -+ (BOOL)checkCameraAuthorizationStatus -{ - if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { - [MBProgressHUD showAutoMessage:@"������������������������" ToView:nil]; - return NO; - } - - if ([AVCaptureDevice respondsToSelector:@selector(authorizationStatusForMediaType:)]) { - AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; - if (AVAuthorizationStatusDenied == authStatus || - AVAuthorizationStatusRestricted == authStatus) { - [MBProgressHUD showAutoMessage:@"������iPhone������������->������->������������������������������������������" ToView:nil]; - return NO; - } - } - return YES; -} - -@end diff --git a/camerademo/camerademo/demo/OtherHelper/dateTimeHelper.h b/camerademo/camerademo/demo/OtherHelper/dateTimeHelper.h deleted file mode 100755 index 7e25da9..0000000 --- a/camerademo/camerademo/demo/OtherHelper/dateTimeHelper.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// dateTimeHelper.h -// MobileProject -// -// Created by wujunyang on 16/7/20. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import <Foundation/Foundation.h> - -@interface dateTimeHelper : NSObject - -//������������������������������ -+ (NSString *)htcTimeToLocationStr:(NSDate*)curDate; - -@end diff --git a/camerademo/camerademo/demo/OtherHelper/dateTimeHelper.m b/camerademo/camerademo/demo/OtherHelper/dateTimeHelper.m deleted file mode 100755 index 0cb4e18..0000000 --- a/camerademo/camerademo/demo/OtherHelper/dateTimeHelper.m +++ /dev/null @@ -1,24 +0,0 @@ -// -// dateTimeHelper.m -// MobileProject -// -// Created by wujunyang on 16/7/20. -// Copyright �� 2016��� wujunyang. All rights reserved. -// - -#import "dateTimeHelper.h" - -@implementation dateTimeHelper - -+ (NSString *)htcTimeToLocationStr:(NSDate*)curDate -{ - if (curDate==nil) { - return @""; - } - NSDateFormatter* dateFormatter = [[NSDateFormatter alloc]init]; - [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; - NSString *fixString = [dateFormatter stringFromDate:curDate]; - return fixString; -} - -@end diff --git a/camerademo/camerademo/demo/OtherHelper/imageCompressHelper.h b/camerademo/camerademo/demo/OtherHelper/imageCompressHelper.h deleted file mode 100755 index e94a550..0000000 --- a/camerademo/camerademo/demo/OtherHelper/imageCompressHelper.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// imageCompressHelper.h -// zxptUser ������������ -// -// Created by wujunyang on 16/4/14. -// Copyright �� 2016��� qijia. All rights reserved. -// - -#import <Foundation/Foundation.h> -#import <AssetsLibrary/ALAsset.h> -#import <AssetsLibrary/ALAssetsLibrary.h> -#import <AssetsLibrary/ALAssetsGroup.h> -#import <AssetsLibrary/ALAssetRepresentation.h> - -@interface imageCompressHelper : NSObject - -/** - * @author wujunyang, 16-04-14 15:04:54 - * - * @brief ������������������KB��������������� - * - * @param kb <#kb description#> - * @param image <#image description#> - * - * @return <#return value description#> - */ -+(UIImage*)compressedImageToLimitSizeOfKB:(CGFloat)kb image:(UIImage*)image; - -+(NSData*)returnDataCompressedImageToLimitSizeOfKB:(CGFloat)kb image:(UIImage*)image; - -//��������������������� ��������������������� -+(UIImage*) circleImage:(UIImage*) image withParam:(CGFloat) inset; - -//��������������������������� -+(UIImage *) imageCompressForWidth:(UIImage *)sourceImage targetWidth:(CGFloat)defineWidth; - -//���������������,size ������������������������ ������������ CGSizeMake(300, 140) -+(UIImage *)compressImage:(UIImage *)sourceImage toTargetWidth:(CGFloat)targetWidth; - -//������������������ -+ (UIImage *)fullScreenImageALAsset:(ALAsset *)asset; -@end diff --git a/camerademo/camerademo/demo/OtherHelper/imageCompressHelper.m b/camerademo/camerademo/demo/OtherHelper/imageCompressHelper.m deleted file mode 100755 index 2852c6a..0000000 --- a/camerademo/camerademo/demo/OtherHelper/imageCompressHelper.m +++ /dev/null @@ -1,163 +0,0 @@ -// -// imageCompressHelper.m -// zxptUser -// -// Created by wujunyang on 16/4/14. -// Copyright �� 2016��� qijia. All rights reserved. -// - -#import "imageCompressHelper.h" - -@implementation imageCompressHelper - -+(UIImage*)compressedImageToLimitSizeOfKB:(CGFloat)kb image:(UIImage*)image -{ - //������������kb��������������������� - long imagePixel = CGImageGetWidth(image.CGImage)*CGImageGetHeight(image.CGImage); - long imageKB = imagePixel * CGImageGetBitsPerPixel(image.CGImage) / (8 * 1024); - if (imageKB > kb){ - float compressedParam = kb / imageKB; - return [UIImage imageWithData:UIImageJPEGRepresentation(image, compressedParam)]; - } - //������������ - else{ - return image; - } -} - -+(NSData*)returnDataCompressedImageToLimitSizeOfKB:(CGFloat)kb image:(UIImage*)image -{ - //������������kb��������������������� - long imagePixel = CGImageGetWidth(image.CGImage)*CGImageGetHeight(image.CGImage); - long imageKB = imagePixel * CGImageGetBitsPerPixel(image.CGImage) / (8 * 1024); - if (imageKB > kb){ - float compressedParam = kb / imageKB; - return UIImageJPEGRepresentation(image, compressedParam); - } - //������������ - else{ - return UIImageJPEGRepresentation(image, 1); - } -} - - -+(UIImage*) circleImage:(UIImage*) image withParam:(CGFloat) inset -{ - //UIGraphicsBeginImageContext(image.size); - //������������ ��������������� - UIGraphicsBeginImageContextWithOptions(image.size, NO, [[UIScreen mainScreen] scale]); - CGContextRef context =UIGraphicsGetCurrentContext(); - - //���������������������2������������������ - - CGContextSetLineWidth(context,2); - - CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor); - - CGRect rect = CGRectMake(inset, inset, image.size.width - inset *2.0f, image.size.height - inset *2.0f); - - CGContextAddEllipseInRect(context, rect); - - CGContextClip(context); - - //���������������������image������ - - [image drawInRect:rect]; - - CGContextAddEllipseInRect(context, rect); - - CGContextStrokePath(context); - - //������������image - - UIImage *newimg = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); - - return newimg; -} - -+(UIImage *) imageCompressForWidth:(UIImage *)sourceImage targetWidth:(CGFloat)defineWidth -{ - UIImage *newImage = nil; - CGSize imageSize = sourceImage.size; - CGFloat width = imageSize.width; - CGFloat height = imageSize.height; - CGFloat targetWidth = defineWidth; - CGFloat targetHeight = height / (width / targetWidth); - CGSize size = CGSizeMake(targetWidth, targetHeight); - CGFloat scaleFactor = 0.0; - CGFloat scaledWidth = targetWidth; - CGFloat scaledHeight = targetHeight; - CGPoint thumbnailPoint = CGPointMake(0.0, 0.0); - - if(CGSizeEqualToSize(imageSize, size) == NO){ - - CGFloat widthFactor = targetWidth / width; - CGFloat heightFactor = targetHeight / height; - - if(widthFactor > heightFactor){ - scaleFactor = widthFactor; - } - else{ - scaleFactor = heightFactor; - } - scaledWidth = width * scaleFactor; - scaledHeight = height * scaleFactor; - - if(widthFactor > heightFactor){ - - thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5; - - }else if(widthFactor < heightFactor){ - - thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5; - } - } - - UIGraphicsBeginImageContext(size); - - CGRect thumbnailRect = CGRectZero; - thumbnailRect.origin = thumbnailPoint; - thumbnailRect.size.width = scaledWidth; - thumbnailRect.size.height = scaledHeight; - - [sourceImage drawInRect:thumbnailRect]; - - newImage = UIGraphicsGetImageFromCurrentImageContext(); - - if(newImage == nil){ - - NSLog(@"scale image fail"); - } - UIGraphicsEndImageContext(); - return newImage; -} - - -+(UIImage *)compressImage:(UIImage *)sourceImage toTargetWidth:(CGFloat)targetWidth -{ - CGSize imageSize = sourceImage.size; - - CGFloat width = imageSize.width; - CGFloat height = imageSize.height; - - CGFloat targetHeight = (targetWidth / width) * height; - - UIGraphicsBeginImageContext(CGSizeMake(targetWidth, targetHeight)); - [sourceImage drawInRect:CGRectMake(0, 0, targetWidth, targetHeight)]; - - UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - return newImage; -} - - -+ (UIImage *)fullScreenImageALAsset:(ALAsset *)asset{ - ALAssetRepresentation *assetRep = [asset defaultRepresentation]; - CGImageRef imgRef = [assetRep fullScreenImage];//fullScreenImage������������������������ - UIImage *img = [UIImage imageWithCGImage:imgRef]; - return img; -} -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionCheckmarkView.h b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionCheckmarkView.h deleted file mode 100755 index 161379c..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionCheckmarkView.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// QBAssetsCollectionCheckmarkView.h -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2014/01/01. -// Copyright (c) 2014��� Katsuma Tanaka. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface QBAssetsCollectionCheckmarkView : UIView - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionCheckmarkView.m b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionCheckmarkView.m deleted file mode 100755 index 82ee42d..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionCheckmarkView.m +++ /dev/null @@ -1,53 +0,0 @@ -// -// QBAssetsCollectionCheckmarkView.m -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2014/01/01. -// Copyright (c) 2014��� Katsuma Tanaka. All rights reserved. -// - -#import "QBAssetsCollectionCheckmarkView.h" - -@implementation QBAssetsCollectionCheckmarkView - -- (instancetype)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - - if (self) { - // View settings - self.backgroundColor = [UIColor clearColor]; - } - - return self; -} - -- (CGSize)sizeThatFits:(CGSize)size -{ - return CGSizeMake(24.0, 24.0); -} - -- (void)drawRect:(CGRect)rect -{ - CGContextRef context = UIGraphicsGetCurrentContext(); - - // Border - CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); - CGContextFillEllipseInRect(context, self.bounds); - - // Body - CGContextSetRGBFillColor(context, 20.0/255.0, 111.0/255.0, 223.0/255.0, 1.0); - CGContextFillEllipseInRect(context, CGRectInset(self.bounds, 1.0, 1.0)); - - // Checkmark - CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0); - CGContextSetLineWidth(context, 1.2); - - CGContextMoveToPoint(context, 6.0, 12.0); - CGContextAddLineToPoint(context, 10.0, 16.0); - CGContextAddLineToPoint(context, 18.0, 8.0); - - CGContextStrokePath(context); -} - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionFooterView.h b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionFooterView.h deleted file mode 100755 index 630307c..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionFooterView.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// QBAssetsCollectionFooterView.h -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/31. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface QBAssetsCollectionFooterView : UICollectionReusableView - -@property (nonatomic, strong, readonly) UILabel *textLabel; - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionFooterView.m b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionFooterView.m deleted file mode 100755 index 3d23af8..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionFooterView.m +++ /dev/null @@ -1,48 +0,0 @@ -// -// QBAssetsCollectionFooterView.m -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/31. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import "QBAssetsCollectionFooterView.h" - -@interface QBAssetsCollectionFooterView () - -@property (nonatomic, strong, readwrite) UILabel *textLabel; - -@end - -@implementation QBAssetsCollectionFooterView - -- (instancetype)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - - if (self) { - // Create a label - UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectZero]; - textLabel.font = [UIFont systemFontOfSize:17]; - textLabel.textColor = [UIColor blackColor]; - textLabel.textAlignment = NSTextAlignmentCenter; - - [self addSubview:textLabel]; - self.textLabel = textLabel; - } - - return self; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - - // Layout text label - self.textLabel.frame = CGRectMake(0, - (self.bounds.size.height - 21.0) / 2.0, - self.bounds.size.width, - 21.0); -} - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionOverlayView.h b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionOverlayView.h deleted file mode 100755 index b4b5b39..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionOverlayView.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// QBAssetsCollectionOverlayView.h -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2014/01/01. -// Copyright (c) 2014��� Katsuma Tanaka. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface QBAssetsCollectionOverlayView : UIView - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionOverlayView.m b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionOverlayView.m deleted file mode 100755 index caa7ae6..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionOverlayView.m +++ /dev/null @@ -1,47 +0,0 @@ -// -// QBAssetsCollectionOverlayView.m -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2014/01/01. -// Copyright (c) 2014��� Katsuma Tanaka. All rights reserved. -// - -#import "QBAssetsCollectionOverlayView.h" -#import <QuartzCore/QuartzCore.h> - -// Views -#import "QBAssetsCollectionCheckmarkView.h" - -@interface QBAssetsCollectionOverlayView () - -@property (nonatomic, strong) QBAssetsCollectionCheckmarkView *checkmarkView; - -@end - -@implementation QBAssetsCollectionOverlayView - -- (instancetype)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - - if (self) { - // View settings - self.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.4]; - - // Create a checkmark view - QBAssetsCollectionCheckmarkView *checkmarkView = [[QBAssetsCollectionCheckmarkView alloc] initWithFrame:CGRectMake(self.bounds.size.width - (4.0 + 24.0), self.bounds.size.height - (4.0 + 24.0), 24.0, 24.0)]; - checkmarkView.autoresizingMask = UIViewAutoresizingNone; - - checkmarkView.layer.shadowColor = [[UIColor grayColor] CGColor]; - checkmarkView.layer.shadowOffset = CGSizeMake(0, 0); - checkmarkView.layer.shadowOpacity = 0.6; - checkmarkView.layer.shadowRadius = 2.0; - - [self addSubview:checkmarkView]; - self.checkmarkView = checkmarkView; - } - - return self; -} - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionVideoIndicatorView.h b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionVideoIndicatorView.h deleted file mode 100755 index 58a78dc..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionVideoIndicatorView.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// QBAssetsCollectionVideoIndicatorView.h -// QBImagePickerControllerDemo -// -// Created by Katsuma Tanaka on 2014/08/07. -// Copyright (c) 2014��� Katsuma Tanaka. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface QBAssetsCollectionVideoIndicatorView : UIView - -@property (nonatomic, assign) NSTimeInterval duration; - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionVideoIndicatorView.m b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionVideoIndicatorView.m deleted file mode 100755 index 4aba36c..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionVideoIndicatorView.m +++ /dev/null @@ -1,107 +0,0 @@ -// -// QBAssetsCollectionVideoIndicatorView.m -// QBImagePickerControllerDemo -// -// Created by Katsuma Tanaka on 2014/08/07. -// Copyright (c) 2014��� Katsuma Tanaka. All rights reserved. -// - -#import "QBAssetsCollectionVideoIndicatorView.h" -#import <QuartzCore/QuartzCore.h> - -@implementation QBAssetsCollectionVideoIndicatorView - -- (instancetype)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - - if (self) { - self.backgroundColor = [UIColor clearColor]; - self.clipsToBounds = YES; - } - - return self; -} - - -#pragma mark - Accessors - -- (void)setDuration:(NSTimeInterval)duration -{ - _duration = duration; - - [self setNeedsDisplay]; -} - - -#pragma mark - Drawing the View - -- (void)drawRect:(CGRect)rect -{ - // Draw linear gradient - CGContextRef context = UIGraphicsGetCurrentContext(); - - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGFloat colors[12] = { - 0.0, 0.0, 0.0, 0.9, - 0.0, 0.0, 0.0, 0.45, - 0.0, 0.0, 0.0, 0.0 - }; - const CGFloat locations[] = { 0.0, 0.55, 1.0 }; - CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, 3); - CGColorSpaceRelease(colorSpace); - - CGPoint startPoint = CGPointMake(0, CGRectGetHeight(self.bounds)); - CGPoint endPoint = CGPointMake(0, 0); - CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation); - - CGGradientRelease(gradient); - - // Draw camera icon - CGSize cameraIconSize = CGSizeMake(14.0, 8.0); - CGRect cameraIconRect = CGRectMake(5.0, (CGRectGetHeight(self.bounds) - cameraIconSize.height) / 2.0, cameraIconSize.width, cameraIconSize.height); - [self drawCameraIconInRect:cameraIconRect]; - - // Draw duration - NSInteger minutes = (NSInteger)(self.duration / 60.0); - NSInteger seconds = (NSInteger)ceil(self.duration - 60.0 * (double)minutes); - NSString *durationString = [NSString stringWithFormat:@"%02ld:%02ld", (long)minutes, (long)seconds]; - - NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - paragraphStyle.alignment = NSTextAlignmentRight; - - UIFont *durationFont = [UIFont systemFontOfSize:12.0]; - - CGRect durationRect = CGRectMake(CGRectGetMaxX(cameraIconRect), 0, CGRectGetWidth(self.bounds) - CGRectGetMaxX(cameraIconRect), CGRectGetHeight(self.bounds)); - durationRect = CGRectInset(durationRect, 5.0, (CGRectGetHeight(self.bounds) - durationFont.lineHeight) / 2.0); - - [durationString drawInRect:durationRect - withAttributes:@{ - NSParagraphStyleAttributeName: [paragraphStyle copy], - NSFontAttributeName: durationFont, - NSForegroundColorAttributeName: [UIColor whiteColor] - }]; -} - -- (void)drawCameraIconInRect:(CGRect)rect -{ - [[UIColor whiteColor] setFill]; - - // Draw triangle - CGRect triangleRect = CGRectMake(0, CGRectGetMinY(rect), ceil(CGRectGetHeight(rect) / 2.0), CGRectGetHeight(rect)); - triangleRect.origin.x = CGRectGetMinX(rect) + CGRectGetWidth(rect) - CGRectGetWidth(triangleRect); - - UIBezierPath *trianglePath = [UIBezierPath bezierPath]; - [trianglePath moveToPoint:CGPointMake(CGRectGetMinX(triangleRect) + CGRectGetWidth(triangleRect), CGRectGetMinY(triangleRect))]; - [trianglePath addLineToPoint:CGPointMake(CGRectGetMinX(triangleRect) + CGRectGetWidth(triangleRect), CGRectGetMaxY(triangleRect))]; - [trianglePath addLineToPoint:CGPointMake(CGRectGetMinX(triangleRect), CGRectGetMidY(triangleRect))]; - [trianglePath closePath]; - [trianglePath fill]; - - // Draw rounded square - CGRect squareRect = CGRectMake(CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetWidth(rect) - CGRectGetWidth(triangleRect) - 1.0, CGRectGetHeight(rect)); - UIBezierPath *squarePath = [UIBezierPath bezierPathWithRoundedRect:squareRect cornerRadius:2.0]; - [squarePath fill]; -} - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewCell.h b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewCell.h deleted file mode 100755 index caba987..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewCell.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// QBAssetsCollectionViewCell.h -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/31. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import <UIKit/UIKit.h> -#import <AssetsLibrary/AssetsLibrary.h> - -@interface QBAssetsCollectionViewCell : UICollectionViewCell - -@property (nonatomic, strong) ALAsset *asset; -@property (nonatomic, assign) BOOL showsOverlayViewWhenSelected; - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewCell.m b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewCell.m deleted file mode 100755 index 502dc93..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewCell.m +++ /dev/null @@ -1,143 +0,0 @@ -// -// QBAssetsCollectionViewCell.m -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/31. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import "QBAssetsCollectionViewCell.h" - -// Views -#import "QBAssetsCollectionOverlayView.h" -#import "QBAssetsCollectionVideoIndicatorView.h" - -@interface QBAssetsCollectionViewCell () - -@property (nonatomic, strong) UIImageView *imageView; -@property (nonatomic, strong) QBAssetsCollectionOverlayView *overlayView; -@property (nonatomic, strong) QBAssetsCollectionVideoIndicatorView *videoIndicatorView; - -@property (nonatomic, strong) UIImage *blankImage; - -@end - -@implementation QBAssetsCollectionViewCell - -- (instancetype)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - - if (self) { - self.showsOverlayViewWhenSelected = YES; - - // Create a image view - UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.contentView.bounds]; - imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - - [self.contentView addSubview:imageView]; - self.imageView = imageView; - } - - return self; -} - -- (void)setSelected:(BOOL)selected -{ - [super setSelected:selected]; - - // Show/hide overlay view - if (selected && self.showsOverlayViewWhenSelected) { - [self showOverlayView]; - } else { - [self hideOverlayView]; - } -} - - -#pragma mark - Overlay View - -- (void)showOverlayView -{ - [self hideOverlayView]; - - QBAssetsCollectionOverlayView *overlayView = [[QBAssetsCollectionOverlayView alloc] initWithFrame:self.contentView.bounds]; - overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - - [self.contentView addSubview:overlayView]; - self.overlayView = overlayView; -} - -- (void)hideOverlayView -{ - if (self.overlayView) { - [self.overlayView removeFromSuperview]; - self.overlayView = nil; - } -} - - -#pragma mark - Video Indicator View - -- (void)showVideoIndicatorView -{ - CGFloat height = 19.0; - CGRect frame = CGRectMake(0, CGRectGetHeight(self.bounds) - height, CGRectGetWidth(self.bounds), height); - QBAssetsCollectionVideoIndicatorView *videoIndicatorView = [[QBAssetsCollectionVideoIndicatorView alloc] initWithFrame:frame]; - videoIndicatorView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - videoIndicatorView.duration = [[self.asset valueForProperty:ALAssetPropertyDuration] doubleValue]; - - [self.contentView addSubview:videoIndicatorView]; - self.videoIndicatorView = videoIndicatorView; -} - -- (void)hideVideoIndicatorView -{ - if (self.videoIndicatorView) { - [self.videoIndicatorView removeFromSuperview]; - self.videoIndicatorView = nil; - } -} - - -#pragma mark - Accessors - -- (void)setAsset:(ALAsset *)asset -{ - _asset = asset; - - // Update view - CGImageRef thumbnailImageRef = [asset thumbnail]; - - if (thumbnailImageRef) { - self.imageView.image = [UIImage imageWithCGImage:thumbnailImageRef]; - } else { - self.imageView.image = [self blankImage]; - } - - // Show video indicator if the asset is video - if ([[asset valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypeVideo]) { - [self showVideoIndicatorView]; - } else { - [self hideVideoIndicatorView]; - } -} - -- (UIImage *)blankImage -{ - if (_blankImage == nil) { - CGSize size = CGSizeMake(100.0, 100.0); - UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); - - [[UIColor colorWithWhite:(240.0 / 255.0) alpha:1.0] setFill]; - UIRectFill(CGRectMake(0, 0, size.width, size.height)); - - _blankImage = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); - } - - return _blankImage; -} - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewController.h b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewController.h deleted file mode 100755 index 036e7f6..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewController.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// QBAssetsCollectionViewController.h -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/31. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import <UIKit/UIKit.h> -#import <AssetsLibrary/AssetsLibrary.h> - -// ViewControllers -#import "QBImagePickerController.h" - -@class QBAssetsCollectionViewController; - -@protocol QBAssetsCollectionViewControllerDelegate <NSObject> - -@optional -- (void)assetsCollectionViewController:(QBAssetsCollectionViewController *)assetsCollectionViewController didSelectAsset:(ALAsset *)asset; -- (void)assetsCollectionViewController:(QBAssetsCollectionViewController *)assetsCollectionViewController didDeselectAsset:(ALAsset *)asset; -- (void)assetsCollectionViewControllerDidFinishSelection:(QBAssetsCollectionViewController *)assetsCollectionViewController; - -@end - -@interface QBAssetsCollectionViewController : UICollectionViewController <UICollectionViewDelegateFlowLayout> - -@property (nonatomic, weak) QBImagePickerController *imagePickerController; - -@property (nonatomic, weak) id<QBAssetsCollectionViewControllerDelegate> delegate; -@property (nonatomic, strong) ALAssetsGroup *assetsGroup; -@property (nonatomic, assign) QBImagePickerControllerFilterType filterType; -@property (nonatomic, assign) BOOL allowsMultipleSelection; -@property (nonatomic, assign) NSUInteger minimumNumberOfSelection; -@property (nonatomic, assign) NSUInteger maximumNumberOfSelection; - -- (void)selectAssetHavingURL:(NSURL *)URL; - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewController.m b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewController.m deleted file mode 100755 index b3fc9c3..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewController.m +++ /dev/null @@ -1,344 +0,0 @@ -// -// QBAssetsCollectionViewController.m -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/31. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import "QBAssetsCollectionViewController.h" - -// Views -#import "QBAssetsCollectionViewCell.h" -#import "QBAssetsCollectionFooterView.h" - -@interface QBAssetsCollectionViewController () - -@property (nonatomic, strong) NSMutableArray *assets; - -@property (nonatomic, assign) NSUInteger numberOfAssets; -@property (nonatomic, assign) NSUInteger numberOfPhotos; -@property (nonatomic, assign) NSUInteger numberOfVideos; - -@property (nonatomic, assign) BOOL disableScrollToBottom; - -@end - -@implementation QBAssetsCollectionViewController - -- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout -{ - self = [super initWithCollectionViewLayout:layout]; - - if (self) { - // View settings - self.collectionView.backgroundColor = [UIColor whiteColor]; - - // Register cell class - [self.collectionView registerClass:[QBAssetsCollectionViewCell class] - forCellWithReuseIdentifier:@"AssetsCell"]; - [self.collectionView registerClass:[QBAssetsCollectionFooterView class] - forSupplementaryViewOfKind:UICollectionElementKindSectionFooter - withReuseIdentifier:@"FooterView"]; - } - - return self; -} - -- (void)viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; - - // Scroll to bottom - if (self.isMovingToParentViewController && !self.disableScrollToBottom) { - CGFloat topInset; - if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { // iOS7 or later - topInset = ((self.edgesForExtendedLayout && UIRectEdgeTop) && (self.collectionView.contentInset.top == 0)) ? (20.0 + 44.0) : 0.0; - } else { - topInset = (self.collectionView.contentInset.top == 0) ? (20.0 + 44.0) : 0.0; - } - - [self.collectionView setContentOffset:CGPointMake(0, self.collectionView.collectionViewLayout.collectionViewContentSize.height - self.collectionView.frame.size.height + topInset) - animated:NO]; - } - - // Validation - if (self.allowsMultipleSelection) { - self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:self.imagePickerController.selectedAssetURLs.count]; - } -} - -- (void)viewWillDisappear:(BOOL)animated -{ - [super viewWillDisappear:animated]; - - self.disableScrollToBottom = YES; -} - -- (void)viewDidDisappear:(BOOL)animated -{ - [super viewDidDisappear:animated]; - - self.disableScrollToBottom = NO; -} - - -#pragma mark - Accessors - -- (void)setFilterType:(QBImagePickerControllerFilterType)filterType -{ - _filterType = filterType; - - // Set assets filter - [self.assetsGroup setAssetsFilter:ALAssetsFilterFromQBImagePickerControllerFilterType(self.filterType)]; -} - -- (void)setAssetsGroup:(ALAssetsGroup *)assetsGroup -{ - _assetsGroup = assetsGroup; - - // Set title - self.title = [self.assetsGroup valueForProperty:ALAssetsGroupPropertyName]; - - // Set assets filter - [self.assetsGroup setAssetsFilter:ALAssetsFilterFromQBImagePickerControllerFilterType(self.filterType)]; - - // Load assets - NSMutableArray *assets = [NSMutableArray array]; - __block NSUInteger numberOfAssets = 0; - __block NSUInteger numberOfPhotos = 0; - __block NSUInteger numberOfVideos = 0; - - [self.assetsGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { - if (result) { - numberOfAssets++; - - NSString *type = [result valueForProperty:ALAssetPropertyType]; - if ([type isEqualToString:ALAssetTypePhoto]) numberOfPhotos++; - else if ([type isEqualToString:ALAssetTypeVideo]) numberOfVideos++; - - [assets addObject:result]; - } - }]; - - self.assets = assets; - self.numberOfAssets = numberOfAssets; - self.numberOfPhotos = numberOfPhotos; - self.numberOfVideos = numberOfVideos; - - // Update view - [self.collectionView reloadData]; -} - -- (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection -{ - self.collectionView.allowsMultipleSelection = allowsMultipleSelection; - - // Show/hide done button - if (allowsMultipleSelection) { - UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(done:)]; - [self.navigationItem setRightBarButtonItem:doneButton animated:NO]; - } else { - [self.navigationItem setRightBarButtonItem:nil animated:NO]; - } -} - -- (BOOL)allowsMultipleSelection -{ - return self.collectionView.allowsMultipleSelection; -} - - -#pragma mark - Actions - -- (void)done:(id)sender -{ - // Delegate - if (self.delegate && [self.delegate respondsToSelector:@selector(assetsCollectionViewControllerDidFinishSelection:)]) { - [self.delegate assetsCollectionViewControllerDidFinishSelection:self]; - } -} - - -#pragma mark - Managing Selection - -- (void)selectAssetHavingURL:(NSURL *)URL -{ - for (NSInteger i = 0; i < self.assets.count; i++) { - ALAsset *asset = self.assets[i]; - NSURL *assetURL = [asset valueForProperty:ALAssetPropertyAssetURL]; - - if ([assetURL isEqual:URL]) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0]; - [self.collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; - - return; - } - } -} - - -#pragma mark - Validating Selections - -- (BOOL)validateNumberOfSelections:(NSUInteger)numberOfSelections -{ - NSUInteger minimumNumberOfSelection = MAX(1, self.minimumNumberOfSelection); - BOOL qualifiesMinimumNumberOfSelection = (numberOfSelections >= minimumNumberOfSelection); - - BOOL qualifiesMaximumNumberOfSelection = YES; - if (minimumNumberOfSelection <= self.maximumNumberOfSelection) { - qualifiesMaximumNumberOfSelection = (numberOfSelections <= self.maximumNumberOfSelection); - } - - return (qualifiesMinimumNumberOfSelection && qualifiesMaximumNumberOfSelection); -} - -- (BOOL)validateMaximumNumberOfSelections:(NSUInteger)numberOfSelections -{ - NSUInteger minimumNumberOfSelection = MAX(1, self.minimumNumberOfSelection); - - if (minimumNumberOfSelection <= self.maximumNumberOfSelection) { - if (numberOfSelections > self.maximumNumberOfSelection) { - [MBProgressHUD showAutoMessage:[NSString stringWithFormat:@"������������������%lu���������", (unsigned long)self.maximumNumberOfSelection]]; - } - return (numberOfSelections <= self.maximumNumberOfSelection); - } - - return YES; -} - - -#pragma mark - UICollectionViewDataSource - -- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView -{ - return 1; -} - -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section -{ - return self.numberOfAssets; -} - -- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath -{ - QBAssetsCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"AssetsCell" forIndexPath:indexPath]; - cell.showsOverlayViewWhenSelected = self.allowsMultipleSelection; - - ALAsset *asset = self.assets[indexPath.row]; - cell.asset = asset; - - return cell; -} - -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section -{ - return CGSizeMake(collectionView.bounds.size.width, 46.0); -} - -- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath -{ - if (kind == UICollectionElementKindSectionFooter) { - QBAssetsCollectionFooterView *footerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter - withReuseIdentifier:@"FooterView" - forIndexPath:indexPath]; - - switch (self.filterType) { - case QBImagePickerControllerFilterTypeNone:{ - NSString *format; - if (self.numberOfPhotos == 1) { - if (self.numberOfVideos == 1) { - format = @"format_photo_and_video"; - } else { - format = @"format_photo_and_videos"; - } - } else if (self.numberOfVideos == 1) { - format = @"format_photos_and_video"; - } else { - format = @"format_photos_and_videos"; - } - footerView.textLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(format, - @"QBImagePickerController", - nil), - self.numberOfPhotos, - self.numberOfVideos - ]; - break; - } - - case QBImagePickerControllerFilterTypePhotos:{ - NSString *format = (self.numberOfPhotos == 1) ? @"format_photo" : @"format_photos"; - footerView.textLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(format, - @"QBImagePickerController", - nil), - self.numberOfPhotos - ]; - break; - } - - case QBImagePickerControllerFilterTypeVideos:{ - NSString *format = (self.numberOfVideos == 1) ? @"format_video" : @"format_videos"; - footerView.textLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(format, - @"QBImagePickerController", - nil), - self.numberOfVideos - ]; - break; - } - } - - return footerView; - } - - return nil; -} - - -#pragma mark - UICollectionViewDelegateFlowLayout - -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath -{ - return CGSizeMake(77.5, 77.5); -} - -- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section -{ - return UIEdgeInsetsMake(2, 2, 2, 2); -} - -- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath -{ - return [self validateMaximumNumberOfSelections:(self.imagePickerController.selectedAssetURLs.count + 1)]; -} - -- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath -{ - ALAsset *asset = self.assets[indexPath.row]; - - // Validation - if (self.allowsMultipleSelection) { - self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:(self.imagePickerController.selectedAssetURLs.count + 1)]; - } - - // Delegate - if (self.delegate && [self.delegate respondsToSelector:@selector(assetsCollectionViewController:didSelectAsset:)]) { - [self.delegate assetsCollectionViewController:self didSelectAsset:asset]; - } -} - -- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath -{ - ALAsset *asset = self.assets[indexPath.row]; - - // Validation - if (self.allowsMultipleSelection) { - self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:(self.imagePickerController.selectedAssetURLs.count - 1)]; - } - - // Delegate - if (self.delegate && [self.delegate respondsToSelector:@selector(assetsCollectionViewController:didDeselectAsset:)]) { - [self.delegate assetsCollectionViewController:self didDeselectAsset:asset]; - } -} - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewLayout.h b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewLayout.h deleted file mode 100755 index f75811e..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewLayout.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// QBAssetsCollectionViewLayout.h -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/31. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface QBAssetsCollectionViewLayout : UICollectionViewFlowLayout - -+ (instancetype)layout; - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewLayout.m b/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewLayout.m deleted file mode 100755 index 4d6f452..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBAssetsCollectionViewLayout.m +++ /dev/null @@ -1,30 +0,0 @@ -// -// QBAssetsCollectionViewLayout.m -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/31. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import "QBAssetsCollectionViewLayout.h" - -@implementation QBAssetsCollectionViewLayout - -+ (instancetype)layout -{ - return [[self alloc] init]; -} - -- (instancetype)init -{ - self = [super init]; - - if (self) { - self.minimumLineSpacing = 2.0; - self.minimumInteritemSpacing = 2.0; - } - - return self; -} - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerController.h b/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerController.h deleted file mode 100755 index 006bf6f..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerController.h +++ /dev/null @@ -1,47 +0,0 @@ -// -// QBImagePickerController.h -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/30. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import <UIKit/UIKit.h> -#import <AssetsLibrary/AssetsLibrary.h> - -typedef NS_ENUM(NSUInteger, QBImagePickerControllerFilterType) { - QBImagePickerControllerFilterTypeNone, - QBImagePickerControllerFilterTypePhotos, - QBImagePickerControllerFilterTypeVideos -}; - -UIKIT_EXTERN ALAssetsFilter * ALAssetsFilterFromQBImagePickerControllerFilterType(QBImagePickerControllerFilterType type); - -@class QBImagePickerController; - -@protocol QBImagePickerControllerDelegate <NSObject> - -@optional -- (void)qb_imagePickerController:(QBImagePickerController *)imagePickerController didSelectAsset:(ALAsset *)asset; -- (void)qb_imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets; -- (void)qb_imagePickerControllerDidCancel:(QBImagePickerController *)imagePickerController; - -@end - -@interface QBImagePickerController : UITableViewController - -@property (nonatomic, strong, readonly) ALAssetsLibrary *assetsLibrary; -@property (nonatomic, copy, readonly) NSArray *assetsGroups; -@property (nonatomic, strong, readonly) NSMutableOrderedSet *selectedAssetURLs; - -@property (nonatomic, weak) id<QBImagePickerControllerDelegate> delegate; -@property (nonatomic, copy) NSArray *groupTypes; -@property (nonatomic, assign) QBImagePickerControllerFilterType filterType; -@property (nonatomic, assign) BOOL showsCancelButton; -@property (nonatomic, assign) BOOL allowsMultipleSelection; -@property (nonatomic, assign) NSUInteger minimumNumberOfSelection; -@property (nonatomic, assign) NSUInteger maximumNumberOfSelection; - -+ (BOOL)isAccessible; - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerController.m b/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerController.m deleted file mode 100755 index 11ec6eb..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerController.m +++ /dev/null @@ -1,405 +0,0 @@ -// -// QBImagePickerController.m -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/30. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import "QBImagePickerController.h" -#import <AssetsLibrary/AssetsLibrary.h> - -// Views -#import "QBImagePickerGroupCell.h" -#import "QBAssetsCollectionViewLayout.h" - -// ViewControllers -#import "QBAssetsCollectionViewController.h" - -ALAssetsFilter * ALAssetsFilterFromQBImagePickerControllerFilterType(QBImagePickerControllerFilterType type) { - switch (type) { - case QBImagePickerControllerFilterTypeNone: - return [ALAssetsFilter allAssets]; - break; - - case QBImagePickerControllerFilterTypePhotos: - return [ALAssetsFilter allPhotos]; - break; - - case QBImagePickerControllerFilterTypeVideos: - return [ALAssetsFilter allVideos]; - break; - } -} - -@interface QBImagePickerController () <QBAssetsCollectionViewControllerDelegate> - -@property (nonatomic, strong, readwrite) ALAssetsLibrary *assetsLibrary; -@property (nonatomic, copy, readwrite) NSArray *assetsGroups; -@property (nonatomic, strong, readwrite) NSMutableOrderedSet *selectedAssetURLs; -@property (nonatomic, assign) BOOL firstShow; - -@end - -@implementation QBImagePickerController - -+ (BOOL)isAccessible -{ - return ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary] && - [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]); -} - -- (instancetype)initWithStyle:(UITableViewStyle)style -{ - self = [super initWithStyle:UITableViewStylePlain]; - - if (self) { - [self setUpProperties]; - } - - return self; -} - -- (void)awakeFromNib -{ - [super awakeFromNib]; - - [self setUpProperties]; -} - -- (void)setUpProperties -{ - // Property settings - self.selectedAssetURLs = [NSMutableOrderedSet orderedSet]; - - self.groupTypes = @[ - @(ALAssetsGroupSavedPhotos), - @(ALAssetsGroupPhotoStream), - @(ALAssetsGroupAlbum) - ]; - self.filterType = QBImagePickerControllerFilterTypeNone; - self.showsCancelButton = YES; - - // View settings - self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; - - // Create assets library instance - ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init]; - self.assetsLibrary = assetsLibrary; - - // Register cell classes - [self.tableView registerClass:[QBImagePickerGroupCell class] forCellReuseIdentifier:@"GroupCell"]; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - // View controller settings - self.title = NSLocalizedStringFromTable(@"title", @"QBImagePickerController", nil); - self.firstShow = YES; -} - -- (void)viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; - - // Load assets groups - [self loadAssetsGroupsWithTypes:self.groupTypes - completion:^(NSArray *assetsGroups) { - self.assetsGroups = assetsGroups; - [self.tableView reloadData]; - if (self.firstShow) { - self.firstShow = NO; -// [self pushToCollectionVCWithIndex:0]; - } - }]; - - // Validation - self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:self.selectedAssetURLs.count]; -} - - -#pragma mark - Accessors - -- (void)setShowsCancelButton:(BOOL)showsCancelButton -{ - _showsCancelButton = showsCancelButton; - - // Show/hide cancel button - if (showsCancelButton) { - UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancel:)]; - [self.navigationItem setLeftBarButtonItem:cancelButton animated:NO]; - } else { - [self.navigationItem setLeftBarButtonItem:nil animated:NO]; - } -} - -- (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection -{ - _allowsMultipleSelection = allowsMultipleSelection; - - // Show/hide done button - if (allowsMultipleSelection) { - UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(done:)]; - [self.navigationItem setRightBarButtonItem:doneButton animated:NO]; - } else { - [self.navigationItem setRightBarButtonItem:nil animated:NO]; - } -} - - -#pragma mark - Actions - -- (void)done:(id)sender -{ - [self passSelectedAssetsToDelegate]; -} - -- (void)cancel:(id)sender -{ - // Delegate - if (self.delegate && [self.delegate respondsToSelector:@selector(qb_imagePickerControllerDidCancel:)]) { - [self.delegate qb_imagePickerControllerDidCancel:self]; - } -} - - -#pragma mark - Validating Selections - -- (BOOL)validateNumberOfSelections:(NSUInteger)numberOfSelections -{ - // Check the number of selected assets - NSUInteger minimumNumberOfSelection = MAX(1, self.minimumNumberOfSelection); - BOOL qualifiesMinimumNumberOfSelection = (numberOfSelections >= minimumNumberOfSelection); - - BOOL qualifiesMaximumNumberOfSelection = YES; - if (minimumNumberOfSelection <= self.maximumNumberOfSelection) { - qualifiesMaximumNumberOfSelection = (numberOfSelections <= self.maximumNumberOfSelection); - } - - return (qualifiesMinimumNumberOfSelection && qualifiesMaximumNumberOfSelection); -} - - -#pragma mark - Managing Assets - -- (void)loadAssetsGroupsWithTypes:(NSArray *)types completion:(void (^)(NSArray *assetsGroups))completion -{ - __block NSMutableArray *assetsGroups = [NSMutableArray array]; - __block NSUInteger numberOfFinishedTypes = 0; - - for (NSNumber *type in types) { - __weak typeof(self) weakSelf = self; - - [self.assetsLibrary enumerateGroupsWithTypes:[type unsignedIntegerValue] - usingBlock:^(ALAssetsGroup *assetsGroup, BOOL *stop) { - if (assetsGroup) { - // Filter the assets group - [assetsGroup setAssetsFilter:ALAssetsFilterFromQBImagePickerControllerFilterType(weakSelf.filterType)]; - - if (assetsGroup.numberOfAssets > 0) { - // Add assets group - [assetsGroups addObject:assetsGroup]; - } - } else { - numberOfFinishedTypes++; - } - - // Check if the loading finished - if (numberOfFinishedTypes == types.count) { - // Sort assets groups - NSArray *sortedAssetsGroups = [self sortAssetsGroups:(NSArray *)assetsGroups typesOrder:types]; - - // Call completion block - if (completion) { - completion(sortedAssetsGroups); - } - } - } failureBlock:^(NSError *error) { - NSLog(@"Error: %@", [error localizedDescription]); - }]; - } -} - -- (NSArray *)sortAssetsGroups:(NSArray *)assetsGroups typesOrder:(NSArray *)typesOrder -{ - NSMutableArray *sortedAssetsGroups = [NSMutableArray array]; - - for (ALAssetsGroup *assetsGroup in assetsGroups) { - if (sortedAssetsGroups.count == 0) { - [sortedAssetsGroups addObject:assetsGroup]; - continue; - } - - ALAssetsGroupType assetsGroupType = [[assetsGroup valueForProperty:ALAssetsGroupPropertyType] unsignedIntegerValue]; - NSUInteger indexOfAssetsGroupType = [typesOrder indexOfObject:@(assetsGroupType)]; - - for (NSInteger i = 0; i <= sortedAssetsGroups.count; i++) { - if (i == sortedAssetsGroups.count) { - [sortedAssetsGroups addObject:assetsGroup]; - break; - } - - ALAssetsGroup *sortedAssetsGroup = sortedAssetsGroups[i]; - ALAssetsGroupType sortedAssetsGroupType = [[sortedAssetsGroup valueForProperty:ALAssetsGroupPropertyType] unsignedIntegerValue]; - NSUInteger indexOfSortedAssetsGroupType = [typesOrder indexOfObject:@(sortedAssetsGroupType)]; - - if (indexOfAssetsGroupType < indexOfSortedAssetsGroupType) { - [sortedAssetsGroups insertObject:assetsGroup atIndex:i]; - break; - } - } - } - - return [sortedAssetsGroups copy]; -} - -- (void)passSelectedAssetsToDelegate -{ - // Load assets from URLs - __block NSMutableArray *assets = [NSMutableArray array]; - __block NSUInteger tryCount = 0; - - for (NSURL *selectedAssetURL in self.selectedAssetURLs) { - __weak typeof(self) weakSelf = self; - - // Success block - void (^selectAsset)(ALAsset *) = ^(ALAsset *asset) { - if (asset) { - [assets addObject:asset]; - } - // Check if the loading finished - if (tryCount == weakSelf.selectedAssetURLs.count) { - //NSLog(@"���������������������������%lu", (long)(tryCount - assets.count)); - // Delegate - if (weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(qb_imagePickerController:didSelectAssets:)]) { - [weakSelf.delegate qb_imagePickerController:weakSelf didSelectAssets:[assets copy]]; - } - } - }; - - [self.assetsLibrary assetForURL:selectedAssetURL resultBlock:^(ALAsset *asset) { - if (asset) { - tryCount++; - selectAsset(asset); - }else{ - // Search in the Photo Stream Album - [weakSelf.assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupPhotoStream usingBlock:^(ALAssetsGroup *group, BOOL *stop) { - [group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stopG) { - if([result.defaultRepresentation.url isEqual:selectedAssetURL]) { - *stopG = YES; - *stop = YES; - tryCount++; - selectAsset(result); - }else if (index == 0){ - NSLog(@"���������"); - tryCount++;//��������������������� - selectAsset(nil); - } - }]; - } failureBlock:^(NSError *error) { - NSLog(@"Error: %@", [error localizedDescription]); - }]; - } - } failureBlock:^(NSError *error) { - NSLog(@"Error: %@", [error localizedDescription]); - }]; - } -} - - -#pragma mark - UITableViewDataSource - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return 1; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - return self.assetsGroups.count; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - QBImagePickerGroupCell *cell = [tableView dequeueReusableCellWithIdentifier:@"GroupCell" forIndexPath:indexPath]; - - ALAssetsGroup *assetsGroup = self.assetsGroups[indexPath.row]; - cell.assetsGroup = assetsGroup; - - return cell; -} - - -#pragma mark - UITableViewDelegate - -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - return 86.0; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - [self pushToCollectionVCWithIndex:indexPath.row]; -} - -- (void)pushToCollectionVCWithIndex:(NSInteger)index{ - if (index >= 0 && index < self.assetsGroups.count) { - QBAssetsCollectionViewController *assetsCollectionViewController = [[QBAssetsCollectionViewController alloc] initWithCollectionViewLayout:[QBAssetsCollectionViewLayout layout]]; - assetsCollectionViewController.imagePickerController = self; - assetsCollectionViewController.filterType = self.filterType; - assetsCollectionViewController.allowsMultipleSelection = self.allowsMultipleSelection; - assetsCollectionViewController.minimumNumberOfSelection = self.minimumNumberOfSelection; - assetsCollectionViewController.maximumNumberOfSelection = self.maximumNumberOfSelection; - - ALAssetsGroup *assetsGroup = self.assetsGroups[index]; - assetsCollectionViewController.delegate = self; - assetsCollectionViewController.assetsGroup = assetsGroup; - - for (NSURL *assetURL in self.selectedAssetURLs) { - [assetsCollectionViewController selectAssetHavingURL:assetURL]; - } - - [self.navigationController pushViewController:assetsCollectionViewController animated:YES]; - } -} - - -#pragma mark - QBAssetsCollectionViewControllerDelegate - -- (void)assetsCollectionViewController:(QBAssetsCollectionViewController *)assetsCollectionViewController didSelectAsset:(ALAsset *)asset -{ - if (self.allowsMultipleSelection) { - // Add asset URL - NSURL *assetURL = [asset valueForProperty:ALAssetPropertyAssetURL]; - [self.selectedAssetURLs addObject:assetURL]; - - // Validation - self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:self.selectedAssetURLs.count]; - } else { - // Delegate - if (self.delegate && [self.delegate respondsToSelector:@selector(qb_imagePickerController:didSelectAsset:)]) { - [self.delegate qb_imagePickerController:self didSelectAsset:asset]; - } - } -} - -- (void)assetsCollectionViewController:(QBAssetsCollectionViewController *)assetsCollectionViewController didDeselectAsset:(ALAsset *)asset -{ - if (self.allowsMultipleSelection) { - // Remove asset URL - NSURL *assetURL = [asset valueForProperty:ALAssetPropertyAssetURL]; - [self.selectedAssetURLs removeObject:assetURL]; - - // Validation - self.navigationItem.rightBarButtonItem.enabled = [self validateNumberOfSelections:self.selectedAssetURLs.count]; - } -} - -- (void)assetsCollectionViewControllerDidFinishSelection:(QBAssetsCollectionViewController *)assetsCollectionViewController -{ - [self passSelectedAssetsToDelegate]; -} - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerController.strings b/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerController.strings deleted file mode 100755 index dfc7cce..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerController.strings +++ /dev/null @@ -1,18 +0,0 @@ -/* - QBImagePickerController.strings - QBImagePickerControllerDemo - - Created by Tanaka Katsuma on 2014/01/01. - Copyright (c) 2014��� Katsuma Tanaka. All rights reserved. -*/ - -"title" = "������"; - -"format_photo" = "���%ld���������"; -"format_photos" = "���%ld���������"; -"format_video" = "%���ld���������"; -"format_videos" = "���%ld���������"; -"format_photo_and_video" = "%���ld���������, %ld���������"; -"format_photos_and_video" = "%���ld���������, %ld���������"; -"format_photo_and_videos" = "%���ld���������, %ld���������"; -"format_photos_and_videos" = "%���ld���������, %ld���������"; diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerGroupCell.h b/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerGroupCell.h deleted file mode 100755 index a6480ae..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerGroupCell.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// QBImagePickerGroupCell.h -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/30. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import <UIKit/UIKit.h> -#import <AssetsLibrary/AssetsLibrary.h> - -@interface QBImagePickerGroupCell : UITableViewCell - -@property (nonatomic, strong) ALAssetsGroup *assetsGroup; - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerGroupCell.m b/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerGroupCell.m deleted file mode 100755 index c5bdac5..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerGroupCell.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// QBImagePickerGroupCell.m -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/30. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import "QBImagePickerGroupCell.h" - -// Views -#import "QBImagePickerThumbnailView.h" - -@interface QBImagePickerGroupCell () - -@property (nonatomic, strong) QBImagePickerThumbnailView *thumbnailView; -@property (nonatomic, strong) UILabel *nameLabel; -@property (nonatomic, strong) UILabel *countLabel; - -@end - -@implementation QBImagePickerGroupCell - -- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier -{ - self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; - - if (self) { - // Cell settings - self.accessoryType = UITableViewCellAccessoryDisclosureIndicator; - - // Create thumbnail view - QBImagePickerThumbnailView *thumbnailView = [[QBImagePickerThumbnailView alloc] initWithFrame:CGRectMake(8, 4, 70, 74)]; - thumbnailView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin; - - [self.contentView addSubview:thumbnailView]; - self.thumbnailView = thumbnailView; - - // Create name label - UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(8 + 70 + 18, 22, 180, 21)]; - nameLabel.font = [UIFont systemFontOfSize:17]; - nameLabel.textColor = [UIColor blackColor]; - nameLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth; - - [self.contentView addSubview:nameLabel]; - self.nameLabel = nameLabel; - - // Create count label - UILabel *countLabel = [[UILabel alloc] initWithFrame:CGRectMake(8 + 70 + 18, 46, 180, 15)]; - countLabel.font = [UIFont systemFontOfSize:12]; - countLabel.textColor = [UIColor blackColor]; - countLabel.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth; - - [self.contentView addSubview:countLabel]; - self.countLabel = countLabel; - } - - return self; -} - - -#pragma mark - Accessors - -- (void)setAssetsGroup:(ALAssetsGroup *)assetsGroup -{ - _assetsGroup = assetsGroup; - - // Update thumbnail view - self.thumbnailView.assetsGroup = self.assetsGroup; - - // Update label - self.nameLabel.text = [self.assetsGroup valueForProperty:ALAssetsGroupPropertyName]; - self.countLabel.text = [NSString stringWithFormat:@"%ld", (long)self.assetsGroup.numberOfAssets]; -} - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerThumbnailView.h b/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerThumbnailView.h deleted file mode 100755 index 2b5eeaa..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerThumbnailView.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// QBImagePickerThumbnailView.h -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/31. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import <UIKit/UIKit.h> -#import <AssetsLibrary/AssetsLibrary.h> - -@interface QBImagePickerThumbnailView : UIView - -@property (nonatomic, strong) ALAssetsGroup *assetsGroup; - -@end diff --git a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerThumbnailView.m b/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerThumbnailView.m deleted file mode 100755 index ea05a6c..0000000 --- a/camerademo/camerademo/demo/QBImagePickerController/QBImagePickerThumbnailView.m +++ /dev/null @@ -1,112 +0,0 @@ -// -// QBImagePickerThumbnailView.m -// QBImagePickerController -// -// Created by Tanaka Katsuma on 2013/12/31. -// Copyright (c) 2013��� Katsuma Tanaka. All rights reserved. -// - -#import "QBImagePickerThumbnailView.h" - -@interface QBImagePickerThumbnailView () - -@property (nonatomic, copy) NSArray *thumbnailImages; -@property (nonatomic, strong) UIImage *blankImage; - -@end - -@implementation QBImagePickerThumbnailView - -- (instancetype)initWithFrame:(CGRect)frame -{ - self = [super initWithFrame:frame]; - - if (self) { - self.backgroundColor = [UIColor clearColor]; - } - - return self; -} - -- (CGSize)sizeThatFits:(CGSize)size -{ - return CGSizeMake(70.0, 74.0); -} - -- (void)drawRect:(CGRect)rect -{ - [super drawRect:rect]; - - CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); - - if (self.thumbnailImages.count == 3) { - UIImage *thumbnailImage = [self.thumbnailImages firstObject]; - - CGRect thumbnailImageRect = CGRectMake(4.0, 0, 62.0, 62.0); - CGContextFillRect(context, thumbnailImageRect); - [thumbnailImage drawInRect:CGRectInset(thumbnailImageRect, 0.5, 0.5)]; - } - if (self.thumbnailImages.count >= 2) { - UIImage *thumbnailImage = self.thumbnailImages[1]; - - CGRect thumbnailImageRect = CGRectMake(2.0, 2.0, 66.0, 66.0); - CGContextFillRect(context, thumbnailImageRect); - [thumbnailImage drawInRect:CGRectInset(thumbnailImageRect, 0.5, 0.5)]; - } - if (self.thumbnailImages.count >= 1) { - UIImage *thumbnailImage = [self.thumbnailImages lastObject]; - - CGRect thumbnailImageRect = CGRectMake(0, 4.0, 70.0, 70.0); - CGContextFillRect(context, thumbnailImageRect); - [thumbnailImage drawInRect:CGRectInset(thumbnailImageRect, 0.5, 0.5)]; - } -} - - -#pragma mark - Accessors - -- (void)setAssetsGroup:(ALAssetsGroup *)assetsGroup -{ - _assetsGroup = assetsGroup; - - // Extract three thumbnail images - NSInteger thumbnailImagesCount = MIN(3, assetsGroup.numberOfAssets); - NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(assetsGroup.numberOfAssets-thumbnailImagesCount, thumbnailImagesCount)]; - NSMutableArray *thumbnailImages = [NSMutableArray array]; - [assetsGroup enumerateAssetsAtIndexes:indexes - options:0 - usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { - if (result) { - CGImageRef thumbnailImageRef = [result thumbnail]; - - if (thumbnailImageRef) { - [thumbnailImages addObject:[UIImage imageWithCGImage:thumbnailImageRef]]; - } else { - [thumbnailImages addObject:[self blankImage]]; - } - } - }]; - self.thumbnailImages = [thumbnailImages copy]; - - [self setNeedsDisplay]; -} - -- (UIImage *)blankImage -{ - if (_blankImage == nil) { - CGSize size = CGSizeMake(100.0, 100.0); - UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); - - [[UIColor colorWithWhite:(240.0 / 255.0) alpha:1.0] setFill]; - UIRectFill(CGRectMake(0, 0, size.width, size.height)); - - _blankImage = UIGraphicsGetImageFromCurrentImageContext(); - - UIGraphicsEndImageContext(); - } - - return _blankImage; -} - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Alpha.h b/camerademo/camerademo/demo/UIImage/UIImage+Alpha.h deleted file mode 100755 index ba09684..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Alpha.h +++ /dev/null @@ -1,40 +0,0 @@ -// UIImage+Alpha.h -// Created by Trevor Harmon on 9/20/09. -// Free for personal or commercial use, with or without modification. -// No warranty is expressed or implied. - -// Helper methods for adding an alpha layer to an image -#import <UIKit/UIKit.h> - -@interface UIImage (Alpha) -/** - * @brief ���������alpha������ - * - * @return ���������alpha������ - */ -- (BOOL)hasAlpha; -/** - * @brief ������������alpha������ ������alpha������ - * - * @return ������������alpha������ ������alpha������ - */ -- (UIImage *)imageWithAlpha; -/** - * @brief ������������������ - * - * @param borderSize ������������ - * - * @return ������������������������������ - */ -- (UIImage *)transparentBorderImage:(NSUInteger)borderSize; - - -//http://stackoverflow.com/questions/6521987/crop-uiimage-to-alpha?answertab=oldest#tab-top -/** - * @brief ������������������������������������ - * - * @return ������������������ - */ -- (UIImage *)trimmedBetterSize; - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Alpha.m b/camerademo/camerademo/demo/UIImage/UIImage+Alpha.m deleted file mode 100755 index fba6259..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Alpha.m +++ /dev/null @@ -1,234 +0,0 @@ -// UIImage+Alpha.m -// Created by Trevor Harmon on 9/20/09. -// Free for personal or commercial use, with or without modification. -// No warranty is expressed or implied. - -#import "UIImage+Alpha.h" - -// Private helper methods -@interface UIImage (AlphaPrivateMethods) -- (CGImageRef)newBorderMask:(NSUInteger)borderSize size:(CGSize)size; -@end - -@implementation UIImage (Alpha) -/** - * @brief ���������alpha������ - * - * @return ���������alpha������ - */ -- (BOOL)hasAlpha { - CGImageAlphaInfo alpha = CGImageGetAlphaInfo(self.CGImage); - return (alpha == kCGImageAlphaFirst || - alpha == kCGImageAlphaLast || - alpha == kCGImageAlphaPremultipliedFirst || - alpha == kCGImageAlphaPremultipliedLast); -} -/** - * @brief ������������alpha������ ������alpha������ - * - * @return ������������alpha������ ������alpha������ - */ -- (UIImage *)imageWithAlpha { - if ([self hasAlpha]) { - return self; - } - - CGImageRef imageRef = self.CGImage; - size_t width = CGImageGetWidth(imageRef); - size_t height = CGImageGetHeight(imageRef); - - // The bitsPerComponent and bitmapInfo values are hard-coded to prevent an "unsupported parameter combination" error - CGContextRef offscreenContext = CGBitmapContextCreate(NULL, - width, - height, - 8, - 0, - CGImageGetColorSpace(imageRef), - kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); - - // Draw the image into the context and retrieve the new image, which will now have an alpha layer - CGContextDrawImage(offscreenContext, CGRectMake(0, 0, width, height), imageRef); - CGImageRef imageRefWithAlpha = CGBitmapContextCreateImage(offscreenContext); - UIImage *imageWithAlpha = [UIImage imageWithCGImage:imageRefWithAlpha]; - - // Clean up - CGContextRelease(offscreenContext); - CGImageRelease(imageRefWithAlpha); - - return imageWithAlpha; -} - -// Returns a copy of the image with a transparent border of the given size added around its edges. -// If the image has no alpha layer, one will be added to it. -/** - * @brief ������������������ - * - * @param borderSize ������������ - * - * @return ������������������������������ - */ -- (UIImage *)transparentBorderImage:(NSUInteger)borderSize { - // If the image does not have an alpha layer, add one - UIImage *image = [self imageWithAlpha]; - - CGRect newRect = CGRectMake(0, 0, image.size.width + borderSize * 2, image.size.height + borderSize * 2); - - // Build a context that's the same dimensions as the new size - CGContextRef bitmap = CGBitmapContextCreate(NULL, - newRect.size.width, - newRect.size.height, - CGImageGetBitsPerComponent(self.CGImage), - 0, - CGImageGetColorSpace(self.CGImage), - CGImageGetBitmapInfo(self.CGImage)); - - // Draw the image in the center of the context, leaving a gap around the edges - CGRect imageLocation = CGRectMake(borderSize, borderSize, image.size.width, image.size.height); - CGContextDrawImage(bitmap, imageLocation, self.CGImage); - CGImageRef borderImageRef = CGBitmapContextCreateImage(bitmap); - - // Create a mask to make the border transparent, and combine it with the image - CGImageRef maskImageRef = [self newBorderMask:borderSize size:newRect.size]; - CGImageRef transparentBorderImageRef = CGImageCreateWithMask(borderImageRef, maskImageRef); - UIImage *transparentBorderImage = [UIImage imageWithCGImage:transparentBorderImageRef]; - - // Clean up - CGContextRelease(bitmap); - CGImageRelease(borderImageRef); - CGImageRelease(maskImageRef); - CGImageRelease(transparentBorderImageRef); - - return transparentBorderImage; -} -/** - * @brief ������������������������������������ - * - * @return ������������������ - */ -- (UIImage *)trimmedBetterSize { - - CGImageRef inImage = self.CGImage; - CFDataRef m_DataRef; - m_DataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage)); - - UInt8 * m_PixelBuf = (UInt8 *) CFDataGetBytePtr(m_DataRef); - -// size_t width = CGImageGetWidth(inImage); -// size_t height = CGImageGetHeight(inImage); - CGFloat width = CGImageGetWidth(inImage); - CGFloat height = CGImageGetHeight(inImage); - CGPoint top,left,right,bottom; - - BOOL breakOut = NO; - for (int x = 0;breakOut==NO && x < width; x++) { - for (int y = 0; y < height; y++) { - int loc = x + (y * width); - loc *= 4; - if (m_PixelBuf[loc + 3] != 0) { - left = CGPointMake(x, y); - breakOut = YES; - break; - } - } - } - - breakOut = NO; - for (int y = 0;breakOut==NO && y < height; y++) { - - for (int x = 0; x < width; x++) { - - int loc = x + (y * width); - loc *= 4; - if (m_PixelBuf[loc + 3] != 0) { - top = CGPointMake(x, y); - breakOut = YES; - break; - } - - } - } - - breakOut = NO; - for (int y = height-1;breakOut==NO && y >= 0; y--) { - - for (int x = width-1; x >= 0; x--) { - - int loc = x + (y * width); - loc *= 4; - if (m_PixelBuf[loc + 3] != 0) { - bottom = CGPointMake(x, y); - breakOut = YES; - break; - } - - } - } - - breakOut = NO; - for (int x = width-1;breakOut==NO && x >= 0; x--) { - - for (int y = height-1; y >= 0; y--) { - - int loc = x + (y * width); - loc *= 4; - if (m_PixelBuf[loc + 3] != 0) { - right = CGPointMake(x, y); - breakOut = YES; - break; - } - - } - } - - - CGFloat scale = self.scale; - - CGRect cropRect = CGRectMake(left.x / scale, top.y/scale, (right.x - left.x)/scale, (bottom.y - top.y) / scale); - UIGraphicsBeginImageContextWithOptions( cropRect.size, - NO, - scale); - [self drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y) - blendMode:kCGBlendModeCopy - alpha:1.]; - UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - CFRelease(m_DataRef); - return croppedImage; -} -#pragma mark - -#pragma mark Private helper methods - -// Creates a mask that makes the outer edges transparent and everything else opaque -// The size must include the entire mask (opaque part + transparent border) -// The caller is responsible for releasing the returned reference by calling CGImageRelease -- (CGImageRef)newBorderMask:(NSUInteger)borderSize size:(CGSize)size { - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); - - // Build a context that's the same dimensions as the new size - CGContextRef maskContext = CGBitmapContextCreate(NULL, - size.width, - size.height, - 8, // 8-bit grayscale - 0, - colorSpace, - kCGBitmapByteOrderDefault | kCGImageAlphaNone); - - // Start with a mask that's entirely transparent - CGContextSetFillColorWithColor(maskContext, [UIColor blackColor].CGColor); - CGContextFillRect(maskContext, CGRectMake(0, 0, size.width, size.height)); - - // Make the inner part (within the border) opaque - CGContextSetFillColorWithColor(maskContext, [UIColor whiteColor].CGColor); - CGContextFillRect(maskContext, CGRectMake(borderSize, borderSize, size.width - borderSize * 2, size.height - borderSize * 2)); - - // Get an image of the context - CGImageRef maskImageRef = CGBitmapContextCreateImage(maskContext); - - // Clean up - CGContextRelease(maskContext); - CGColorSpaceRelease(colorSpace); - - return maskImageRef; -} - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+BetterFace.h b/camerademo/camerademo/demo/UIImage/UIImage+BetterFace.h deleted file mode 100755 index 95fa0d6..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+BetterFace.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// UIImage+BetterFace.h -// bf -// -// Created by liuyan on 13-11-25. -// Copyright (c) 2013��� Croath. All rights reserved. -// -// https://github.com/croath/UIImageView-BetterFace -// a UIImageView category to let the picture-cutting with faces showing better - -#import <UIKit/UIKit.h> - -typedef NS_ENUM(NSUInteger, BFAccuracy) { - kBFAccuracyLow = 0, - kBFAccuracyHigh, -}; - -@interface UIImage (BetterFace) - -- (UIImage *)betterFaceImageForSize:(CGSize)size - accuracy:(BFAccuracy)accurary; - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+BetterFace.m b/camerademo/camerademo/demo/UIImage/UIImage+BetterFace.m deleted file mode 100755 index f776f71..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+BetterFace.m +++ /dev/null @@ -1,111 +0,0 @@ -// -// UIImage+BetterFace.m -// bf -// -// Created by liuyan on 13-11-25. -// Copyright (c) 2013��� Croath. All rights reserved. -// - -#import "UIImage+BetterFace.h" - -#define GOLDEN_RATIO (0.618) - -#ifdef BF_DEBUG -#define BFLog(format...) NSLog(format) -#else -#define BFLog(format...) -#endif - -@implementation UIImage (BetterFace) - -- (UIImage *)betterFaceImageForSize:(CGSize)size - accuracy:(BFAccuracy)accurary; -{ - NSArray *features = [UIImage _faceFeaturesInImage:self accuracy:accurary]; - - if ([features count]==0) { - BFLog(@"no faces"); - return nil; - } else { - BFLog(@"succeed %lu faces", (unsigned long)[features count]); - return [self _subImageForFaceFeatures:features - size:size]; - } -} - -- (UIImage *)_subImageForFaceFeatures:(NSArray *)faceFeatures size:(CGSize)size -{ - CGRect fixedRect = CGRectMake(MAXFLOAT, MAXFLOAT, 0, 0); - CGFloat rightBorder = 0, bottomBorder = 0; - for (CIFaceFeature *faceFeature in faceFeatures){ - CGRect oneRect = faceFeature.bounds; - oneRect.origin.y = size.height - oneRect.origin.y - oneRect.size.height; - - fixedRect.origin.x = MIN(oneRect.origin.x, fixedRect.origin.x); - fixedRect.origin.y = MIN(oneRect.origin.y, fixedRect.origin.y); - - rightBorder = MAX(oneRect.origin.x + oneRect.size.width, rightBorder); - bottomBorder = MAX(oneRect.origin.y + oneRect.size.height, bottomBorder); - } - - fixedRect.size.width = rightBorder - fixedRect.origin.x; - fixedRect.size.height = bottomBorder - fixedRect.origin.y; - - CGPoint fixedCenter = CGPointMake(fixedRect.origin.x + fixedRect.size.width / 2.0, - fixedRect.origin.y + fixedRect.size.height / 2.0); - CGPoint offset = CGPointZero; - CGSize finalSize = size; - if (size.width / size.height > self.size.width / self.size.height) { - //move horizonal - finalSize.height = self.size.height; - finalSize.width = size.width/size.height * finalSize.height; - fixedCenter.x = finalSize.width / size.width * fixedCenter.x; - fixedCenter.y = finalSize.width / size.width * fixedCenter.y; - - offset.x = fixedCenter.x - self.size.width * 0.5; - if (offset.x < 0) { - offset.x = 0; - } else if (offset.x + self.size.width > finalSize.width) { - offset.x = finalSize.width - self.size.width; - } - offset.x = - offset.x; - } else { - //move vertical - finalSize.width = self.size.width; - finalSize.height = size.height/size.width * finalSize.width; - fixedCenter.x = finalSize.width / size.width * fixedCenter.x; - fixedCenter.y = finalSize.width / size.width * fixedCenter.y; - - offset.y = fixedCenter.y - self.size.height * (1-GOLDEN_RATIO); - if (offset.y < 0) { - offset.y = 0; - } else if (offset.y + self.size.height > finalSize.height){ - offset.y = finalSize.height = self.size.height; - } - offset.y = - offset.y; - } - - CGRect finalRect = CGRectApplyAffineTransform(CGRectMake(offset.x, offset.y, finalSize.width, finalSize.height), - CGAffineTransformMakeScale(self.scale, self.scale)); - CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], finalRect); - UIImage *subImage = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation]; - CGImageRelease(imageRef); - - return subImage; -} - -#pragma mark - Util - -+ (NSArray *)_faceFeaturesInImage:(UIImage *)image accuracy:(BFAccuracy)accurary -{ - CIImage *ciImage = [CIImage imageWithCGImage:image.CGImage]; - NSString *accuraryStr = (accurary == kBFAccuracyLow) ? CIDetectorAccuracyLow : CIDetectorAccuracyHigh; - - CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace - context:nil - options:@{CIDetectorAccuracy: accuraryStr}]; - - return [detector featuresInImage:ciImage]; -} - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Blur.h b/camerademo/camerademo/demo/UIImage/UIImage+Blur.h deleted file mode 100755 index 4f5f82b..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Blur.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// UIImage+Blur.h -// IOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 15/6/5. -// Copyright (c) 2015��� www.skyfox.org. All rights reserved. -// - -#import <UIKit/UIKit.h> -FOUNDATION_EXPORT double ImageEffectsVersionNumber; -FOUNDATION_EXPORT const unsigned char ImageEffectsVersionString[]; -@interface UIImage (Blur) -- (UIImage *)lightImage; -- (UIImage *)extraLightImage; -- (UIImage *)darkImage; -- (UIImage *)tintedImageWithColor:(UIColor *)tintColor; - -- (UIImage *)blurredImageWithRadius:(CGFloat)blurRadius; -- (UIImage *)blurredImageWithSize:(CGSize)blurSize; -- (UIImage *)blurredImageWithSize:(CGSize)blurSize - tintColor:(UIColor *)tintColor - saturationDeltaFactor:(CGFloat)saturationDeltaFactor - maskImage:(UIImage *)maskImage; -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Blur.m b/camerademo/camerademo/demo/UIImage/UIImage+Blur.m deleted file mode 100755 index 70f4461..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Blur.m +++ /dev/null @@ -1,281 +0,0 @@ -// -// UIImage+Blur.m -// IOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 15/6/5. -// Copyright (c) 2015��� www.skyfox.org. All rights reserved. -// - -#import "UIImage+Blur.h" -@import Accelerate; -@implementation UIImage (Blur) -#pragma mark - -#pragma mark - Effects - -//| ---------------------------------------------------------------------------- -- (UIImage *)lightImage -{ - UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3]; - return [self blurredImageWithSize:CGSizeMake(60, 60) tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil]; -} - - -//| ---------------------------------------------------------------------------- -- (UIImage *)extraLightImage -{ - UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82]; - return [self blurredImageWithSize:CGSizeMake(40, 40) tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil]; -} - - -//| ---------------------------------------------------------------------------- -- (UIImage *)darkImage -{ - UIColor *tintColor = [UIColor colorWithWhite:0.11 alpha:0.73]; - return [self blurredImageWithSize:CGSizeMake(40, 40) tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil]; -} - - -//| ---------------------------------------------------------------------------- -- (UIImage *)tintedImageWithColor:(UIColor *)tintColor -{ - const CGFloat EffectColorAlpha = 0.6; - UIColor *effectColor = tintColor; - size_t componentCount = CGColorGetNumberOfComponents(tintColor.CGColor); - if (componentCount == 2) { - CGFloat b; - if ([tintColor getWhite:&b alpha:NULL]) { - effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha]; - } - } - else { - CGFloat r, g, b; - if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) { - effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha]; - } - } - return [self blurredImageWithSize:CGSizeMake(20, 20) tintColor:effectColor saturationDeltaFactor:-1.0 maskImage:nil]; -} - - -//| ---------------------------------------------------------------------------- -- (UIImage *)blurredImageWithRadius:(CGFloat)blurRadius -{ - return [self blurredImageWithSize:CGSizeMake(blurRadius, blurRadius)]; -} - - -//| ---------------------------------------------------------------------------- -- (UIImage *)blurredImageWithSize:(CGSize)blurSize -{ - return [self blurredImageWithSize:blurSize tintColor:nil saturationDeltaFactor:1.0 maskImage:nil]; -} - -#pragma mark - -#pragma mark - Implementation - -//| ---------------------------------------------------------------------------- -- (UIImage *)blurredImageWithSize:(CGSize)blurSize tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage -{ -#define ENABLE_BLUR 1 -#define ENABLE_SATURATION_ADJUSTMENT 1 -#define ENABLE_TINT 1 - - // Check pre-conditions. - if (self.size.width < 1 || self.size.height < 1) - { - NSLog(@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self); - return nil; - } - if (!self.CGImage) - { - NSLog(@"*** error: inputImage must be backed by a CGImage: %@", self); - return nil; - } - if (maskImage && !maskImage.CGImage) - { - NSLog(@"*** error: effectMaskImage must be backed by a CGImage: %@", maskImage); - return nil; - } - - BOOL hasBlur = blurSize.width > __FLT_EPSILON__ || blurSize.height > __FLT_EPSILON__; - BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__; - - CGImageRef inputCGImage = self.CGImage; - CGFloat inputImageScale = self.scale; - CGBitmapInfo inputImageBitmapInfo = CGImageGetBitmapInfo(inputCGImage); - CGImageAlphaInfo inputImageAlphaInfo = (inputImageBitmapInfo & kCGBitmapAlphaInfoMask); - - CGSize outputImageSizeInPoints = self.size; - CGRect outputImageRectInPoints = { CGPointZero, outputImageSizeInPoints }; - - // Set up output context. - BOOL useOpaqueContext; - if (inputImageAlphaInfo == kCGImageAlphaNone || inputImageAlphaInfo == kCGImageAlphaNoneSkipLast || inputImageAlphaInfo == kCGImageAlphaNoneSkipFirst) - useOpaqueContext = YES; - else - useOpaqueContext = NO; - UIGraphicsBeginImageContextWithOptions(outputImageRectInPoints.size, useOpaqueContext, inputImageScale); - CGContextRef outputContext = UIGraphicsGetCurrentContext(); - CGContextScaleCTM(outputContext, 1.0, -1.0); - CGContextTranslateCTM(outputContext, 0, -outputImageRectInPoints.size.height); - - if (hasBlur || hasSaturationChange) - { - vImage_Buffer effectInBuffer; - vImage_Buffer scratchBuffer1; - - vImage_Buffer *inputBuffer; - vImage_Buffer *outputBuffer; - - vImage_CGImageFormat format = { - .bitsPerComponent = 8, - .bitsPerPixel = 32, - .colorSpace = NULL, - // (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) - // requests a BGRA buffer. - .bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little, - .version = 0, - .decode = NULL, - .renderingIntent = kCGRenderingIntentDefault - }; - - vImage_Error e = vImageBuffer_InitWithCGImage(&effectInBuffer, &format, NULL, self.CGImage, kvImagePrintDiagnosticsToConsole); - if (e != kvImageNoError) - { - NSLog(@"*** error: vImageBuffer_InitWithCGImage returned error code %zi for inputImage: %@", e, self); - UIGraphicsEndImageContext(); - return nil; - } - - vImageBuffer_Init(&scratchBuffer1, effectInBuffer.height, effectInBuffer.width, format.bitsPerPixel, kvImageNoFlags); - inputBuffer = &effectInBuffer; - outputBuffer = &scratchBuffer1; - -#if ENABLE_BLUR - if (hasBlur) - { - CGFloat radiusX = [self gaussianBlurRadiusWithBlurRadius:blurSize.width * inputImageScale]; - CGFloat radiusY = [self gaussianBlurRadiusWithBlurRadius:blurSize.height * inputImageScale]; - - NSInteger tempBufferSize = vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, NULL, 0, 0, radiusY, radiusX, NULL, kvImageGetTempBufferSize | kvImageEdgeExtend); - void *tempBuffer = malloc(tempBufferSize); - - vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, tempBuffer, 0, 0, radiusY, radiusX, NULL, kvImageEdgeExtend); - vImageBoxConvolve_ARGB8888(outputBuffer, inputBuffer, tempBuffer, 0, 0, radiusY, radiusX, NULL, kvImageEdgeExtend); - vImageBoxConvolve_ARGB8888(inputBuffer, outputBuffer, tempBuffer, 0, 0, radiusY, radiusX, NULL, kvImageEdgeExtend); - - free(tempBuffer); - - vImage_Buffer *temp = inputBuffer; - inputBuffer = outputBuffer; - outputBuffer = temp; - } -#endif - -#if ENABLE_SATURATION_ADJUSTMENT - if (hasSaturationChange) - { - CGFloat s = saturationDeltaFactor; - // These values appear in the W3C Filter Effects spec: - // https://dvcs.w3.org/hg/FXTF/raw-file/default/filters/index.html#grayscaleEquivalent - // - CGFloat floatingPointSaturationMatrix[] = { - 0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0, - 0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0, - 0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0, - 0, 0, 0, 1, - }; - const int32_t divisor = 256; - NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]); - int16_t saturationMatrix[matrixSize]; - for (NSUInteger i = 0; i < matrixSize; ++i) { - saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor); - } - vImageMatrixMultiply_ARGB8888(inputBuffer, outputBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags); - - vImage_Buffer *temp = inputBuffer; - inputBuffer = outputBuffer; - outputBuffer = temp; - } -#endif - - CGImageRef effectCGImage; - if ( (effectCGImage = vImageCreateCGImageFromBuffer(inputBuffer, &format, &cleanupBuffer, NULL, kvImageNoAllocate, NULL)) == NULL ) { - effectCGImage = vImageCreateCGImageFromBuffer(inputBuffer, &format, NULL, NULL, kvImageNoFlags, NULL); - free(inputBuffer->data); - } - if (maskImage) { - // Only need to draw the base image if the effect image will be masked. - CGContextDrawImage(outputContext, outputImageRectInPoints, inputCGImage); - } - - // draw effect image - CGContextSaveGState(outputContext); - if (maskImage) - CGContextClipToMask(outputContext, outputImageRectInPoints, maskImage.CGImage); - CGContextDrawImage(outputContext, outputImageRectInPoints, effectCGImage); - CGContextRestoreGState(outputContext); - - // Cleanup - CGImageRelease(effectCGImage); - free(outputBuffer->data); - } - else - { - // draw base image - CGContextDrawImage(outputContext, outputImageRectInPoints, inputCGImage); - } - -#if ENABLE_TINT - // Add in color tint. - if (tintColor) - { - CGContextSaveGState(outputContext); - CGContextSetFillColorWithColor(outputContext, tintColor.CGColor); - CGContextFillRect(outputContext, outputImageRectInPoints); - CGContextRestoreGState(outputContext); - } -#endif - - // Output image is ready. - UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - return outputImage; -#undef ENABLE_BLUR -#undef ENABLE_SATURATION_ADJUSTMENT -#undef ENABLE_TINT -} - - -// A description of how to compute the box kernel width from the Gaussian -// radius (aka standard deviation) appears in the SVG spec: -// http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement -// -// For larger values of 's' (s >= 2.0), an approximation can be used: Three -// successive box-blurs build a piece-wise quadratic convolution kernel, which -// approximates the Gaussian kernel to within roughly 3%. -// -// let d = floor(s * 3*sqrt(2*pi)/4 + 0.5) -// -// ... if d is odd, use three box-blurs of size 'd', centered on the output pixel. -// -- (CGFloat)gaussianBlurRadiusWithBlurRadius:(CGFloat)blurRadius -{ - if (blurRadius - 2. < __FLT_EPSILON__) { - blurRadius = 2.; - } - uint32_t radius = floor((blurRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5) / 2); - radius |= 1; // force radius to be odd so that the three box-blur methodology works. - return radius; -} - - -//| ---------------------------------------------------------------------------- -// Helper function to handle deferred cleanup of a buffer. -// -void cleanupBuffer(void *userData, void *buf_data) -{ free(buf_data); } - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Capture.h b/camerademo/camerademo/demo/UIImage/UIImage+Capture.h deleted file mode 100755 index 36ccac3..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Capture.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// UIImage+Capture.h -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 15/1/10. -// Copyright (c) 2015��� www.skyfox.org. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface UIImage (Capture) -/** - * @brief ������������view��������� - * - * @param view ������view - * - * @return ������ - */ -+ (UIImage *)captureWithView:(UIView *)view; - -///��������������������������������� -+ (UIImage *)getImageWithSize:(CGRect)myImageRect FromImage:(UIImage *)bigImage; - -/** - * @author Jakey - * - * @brief ������������view��������������� ������������������������ - * - * @param aView ���������view - * @param maxWidth ������������ 0���view������������ - * - * @return ������ - */ -+ (UIImage *)screenshotWithView:(UIView *)aView limitWidth:(CGFloat)maxWidth; -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Capture.m b/camerademo/camerademo/demo/UIImage/UIImage+Capture.m deleted file mode 100755 index e376c89..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Capture.m +++ /dev/null @@ -1,105 +0,0 @@ -// -// UIImage+Capture.m -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 15/1/10. -// Copyright (c) 2015��� www.skyfox.org. All rights reserved. -// - -#import "UIImage+Capture.h" -#import <QuartzCore/QuartzCore.h> -@implementation UIImage (Capture) -/** - * @brief ������������view��������� - * - * @param view ������view - * - * @return ������ - */ -+ (UIImage *)captureWithView:(UIView *)view -{ - UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, [UIScreen mainScreen].scale); - // IOS7������������������ - if ([view respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) { - [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO]; - } else { // IOS7��������������� - [view.layer renderInContext:UIGraphicsGetCurrentContext()]; - } - - UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return screenshot; -} - -+ (UIImage *)getImageWithSize:(CGRect)myImageRect FromImage:(UIImage *)bigImage -{ - //������bigImage - //������myImageRect������������������ - CGImageRef imageRef = bigImage.CGImage; - CGImageRef subImageRef = CGImageCreateWithImageInRect(imageRef, myImageRect); - CGSize size; - size.width = CGRectGetWidth(myImageRect); - size.height = CGRectGetHeight(myImageRect); - UIGraphicsBeginImageContext(size); - CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextDrawImage(context, myImageRect, subImageRef); - UIImage* smallImage = [UIImage imageWithCGImage:subImageRef]; - CGImageRelease(subImageRef); - UIGraphicsEndImageContext(); - return smallImage; -} - -/** - * @author Jakey - * - * @brief ������������view��������������� ������������������������ - * - * @param aView ���������view - * @param maxWidth ������������ 0���view������������ - * - * @return ������ - */ -+ (UIImage *)screenshotWithView:(UIView *)aView limitWidth:(CGFloat)maxWidth{ - CGAffineTransform oldTransform = aView.transform; - - CGAffineTransform scaleTransform = CGAffineTransformIdentity; - if (!isnan(maxWidth) && maxWidth>0) { - CGFloat maxScale = maxWidth/CGRectGetWidth(aView.frame); - CGAffineTransform transformScale = CGAffineTransformMakeScale(maxScale, maxScale); - scaleTransform = CGAffineTransformConcat(oldTransform, transformScale); - - } - if(!CGAffineTransformEqualToTransform(scaleTransform, CGAffineTransformIdentity)){ - aView.transform = scaleTransform; - } - - CGRect actureFrame = aView.frame; //���������������������frame - CGRect actureBounds= aView.bounds;//CGRectApplyAffineTransform(); - - //begin - UIGraphicsBeginImageContextWithOptions(actureFrame.size, NO, 0.0); - CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextSaveGState(context); - // CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1, -1); - CGContextTranslateCTM(context,actureFrame.size.width/2, actureFrame.size.height/2); - CGContextConcatCTM(context, aView.transform); - CGPoint anchorPoint = aView.layer.anchorPoint; - CGContextTranslateCTM(context, - -actureBounds.size.width * anchorPoint.x, - -actureBounds.size.height * anchorPoint.y); - if([aView respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) - { - [aView drawViewHierarchyInRect:aView.bounds afterScreenUpdates:NO]; - } - else - { - [aView.layer renderInContext:UIGraphicsGetCurrentContext()]; - } - UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - //end - aView.transform = oldTransform; - - return screenshot; -} -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Color.h b/camerademo/camerademo/demo/UIImage/UIImage+Color.h deleted file mode 100755 index 10fefa6..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Color.h +++ /dev/null @@ -1,53 +0,0 @@ -// -// UIImage+Color.h -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 14/12/15. -// Copyright (c) 2014��� www.skyfox.org. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface UIImage (Color) -/** - * @brief ������������������������������ - * - * @param color ������ - * - * @return ������������ - */ -+ (UIImage *)imageWithColor:(UIColor *)color; -/** - * @brief ��������������������������� - * - * @param point ��������� - * - * @return ������ - */ -- (UIColor *)colorAtPoint:(CGPoint )point; -//more accurate method ,colorAtPixel 1x1 pixel -/** - * @brief ������������������������ - * - * @param point ��������� - * - * @return ������ - */ -- (UIColor *)colorAtPixel:(CGPoint)point; -/** - * @brief ��������������������������������������� - * - * @return ������������������������ - */ -- (BOOL)hasAlphaChannel; - -/** - * @brief ��������������� - * - * @param sourceImage ������ - * - * @return ������������������ - */ -+ (UIImage*)covertToGrayImageFromImage:(UIImage*)sourceImage; - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Color.m b/camerademo/camerademo/demo/UIImage/UIImage+Color.m deleted file mode 100755 index e005574..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Color.m +++ /dev/null @@ -1,175 +0,0 @@ -// -// UIImage+Color.m -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 14/12/15. -// Copyright (c) 2014��� www.skyfox.org. All rights reserved. -// - -#import "UIImage+Color.h" - -@implementation UIImage (Color) -/** - * @brief ������������������������������ - * - * @param color ������ - * - * @return ������������ - */ -+ (UIImage *)imageWithColor:(UIColor *)color { - CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); - UIGraphicsBeginImageContext(rect.size); - CGContextRef context = UIGraphicsGetCurrentContext(); - - CGContextSetFillColorWithColor(context, [color CGColor]); - CGContextFillRect(context, rect); - - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - return image; -} -/** - * @brief ��������������������������� - * - * @param point ��������� - * - * @return ������ - */ -- (UIColor *)colorAtPoint:(CGPoint )point -{ - if (point.x < 0 || point.y < 0) return nil; - - CGImageRef imageRef = self.CGImage; - NSUInteger width = CGImageGetWidth(imageRef); - NSUInteger height = CGImageGetHeight(imageRef); - if (point.x >= width || point.y >= height) return nil; - - unsigned char *rawData = malloc(height * width * 4); - if (!rawData) return nil; - - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - NSUInteger bytesPerPixel = 4; - NSUInteger bytesPerRow = bytesPerPixel * width; - NSUInteger bitsPerComponent = 8; - CGContextRef context = CGBitmapContextCreate(rawData, - width, - height, - bitsPerComponent, - bytesPerRow, - colorSpace, - kCGImageAlphaPremultipliedLast - | kCGBitmapByteOrder32Big); - if (!context) { - free(rawData); - return nil; - } - CGColorSpaceRelease(colorSpace); - CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); - CGContextRelease(context); - - int byteIndex = (bytesPerRow * point.y) + point.x * bytesPerPixel; - CGFloat red = (rawData[byteIndex] * 1.0) / 255.0; - CGFloat green = (rawData[byteIndex + 1] * 1.0) / 255.0; - CGFloat blue = (rawData[byteIndex + 2] * 1.0) / 255.0; - CGFloat alpha = (rawData[byteIndex + 3] * 1.0) / 255.0; - - UIColor *result = nil; - result = [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; - free(rawData); - return result; -} -/** - * @brief ������������������������ - * - * @param point ��������� - * - * @return ������ - */ -- (UIColor *)colorAtPixel:(CGPoint)point -{ - // Cancel if point is outside image coordinates - if (!CGRectContainsPoint(CGRectMake(0.0f, 0.0f, self.size.width, self.size.height), point)) { - return nil; - } - - // Create a 1x1 pixel byte array and bitmap context to draw the pixel into. - // Reference: http://stackoverflow.com/questions/1042830/retrieving-a-pixel-alpha-value-for-a-uiimage - NSInteger pointX = trunc(point.x); - NSInteger pointY = trunc(point.y); - CGImageRef cgImage = self.CGImage; - NSUInteger width = self.size.width; - NSUInteger height = self.size.height; - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - int bytesPerPixel = 4; - int bytesPerRow = bytesPerPixel * 1; - NSUInteger bitsPerComponent = 8; - unsigned char pixelData[4] = { 0, 0, 0, 0 }; - CGContextRef context = CGBitmapContextCreate(pixelData, - 1, - 1, - bitsPerComponent, - bytesPerRow, - colorSpace, - kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); - CGColorSpaceRelease(colorSpace); - CGContextSetBlendMode(context, kCGBlendModeCopy); - - // Draw the pixel we are interested in onto the bitmap context - CGContextTranslateCTM(context, -pointX, pointY-(CGFloat)height); - CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, (CGFloat)width, (CGFloat)height), cgImage); - CGContextRelease(context); - - // Convert color values [0..255] to floats [0.0..1.0] - CGFloat red = (CGFloat)pixelData[0] / 255.0f; - CGFloat green = (CGFloat)pixelData[1] / 255.0f; - CGFloat blue = (CGFloat)pixelData[2] / 255.0f; - CGFloat alpha = (CGFloat)pixelData[3] / 255.0f; - return [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; -} -/** - * @brief ��������������������������������������� - * - * @return ������������������������ - */ -- (BOOL)hasAlphaChannel -{ - CGImageAlphaInfo alpha = CGImageGetAlphaInfo(self.CGImage); - return (alpha == kCGImageAlphaFirst || - alpha == kCGImageAlphaLast || - alpha == kCGImageAlphaPremultipliedFirst || - alpha == kCGImageAlphaPremultipliedLast); -} - -/** - * @brief ��������������� - * - * @param sourceImage ������ - * - * @return ������������������ - */ - -+ (UIImage*)covertToGrayImageFromImage:(UIImage*)sourceImage -{ - int width = sourceImage.size.width; - int height = sourceImage.size.height; - - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); - CGContextRef context = CGBitmapContextCreate (nil,width,height,8,0,colorSpace,kCGImageAlphaNone); - CGColorSpaceRelease(colorSpace); - - if (context == NULL) { - return nil; - } - - CGContextDrawImage(context,CGRectMake(0, 0, width, height), sourceImage.CGImage); - CGImageRef contextRef = CGBitmapContextCreateImage(context); - UIImage *grayImage = [UIImage imageWithCGImage:contextRef]; - CGContextRelease(context); - CGImageRelease(contextRef); - - return grayImage; -} - - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+FX.h b/camerademo/camerademo/demo/UIImage/UIImage+FX.h deleted file mode 100755 index 1eacf3d..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+FX.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// UIImage+FX.h -// -// Version 1.2.3 -// -// Created by Nick Lockwood on 31/10/2011. -// Copyright (c) 2011 Charcoal Design -// -// Distributed under the permissive zlib License -// Get the latest version from here: -// -// https://github.com/nicklockwood/FXImageView -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// - -#import <UIKit/UIKit.h> - - -@interface UIImage (FX) - -- (UIImage *)imageCroppedToRect:(CGRect)rect; -- (UIImage *)imageScaledToSize:(CGSize)size; -- (UIImage *)imageScaledToFitSize:(CGSize)size; -- (UIImage *)imageScaledToFillSize:(CGSize)size; -- (UIImage *)imageCroppedAndScaledToSize:(CGSize)size - contentMode:(UIViewContentMode)contentMode - padToFit:(BOOL)padToFit; - -- (UIImage *)reflectedImageWithScale:(CGFloat)scale; -- (UIImage *)imageWithReflectionWithScale:(CGFloat)scale gap:(CGFloat)gap alpha:(CGFloat)alpha; -- (UIImage *)imageWithShadowColor:(UIColor *)color offset:(CGSize)offset blur:(CGFloat)blur; -- (UIImage *)imageWithCornerRadius:(CGFloat)radius; -- (UIImage *)imageWithAlpha:(CGFloat)alpha; -- (UIImage *)imageWithMask:(UIImage *)maskImage; - -- (UIImage *)maskImageFromImageAlpha; - - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+FX.m b/camerademo/camerademo/demo/UIImage/UIImage+FX.m deleted file mode 100755 index 904a1f3..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+FX.m +++ /dev/null @@ -1,426 +0,0 @@ -// -// UIImage+FX.m -// -// Version 1.2.3 -// -// Created by Nick Lockwood on 31/10/2011. -// Copyright (c) 2011 Charcoal Design -// -// Distributed under the permissive zlib License -// Get the latest version from here: -// -// https://github.com/nicklockwood/FXImageView -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// - -#import "UIImage+FX.h" - -@implementation UIImage (FX) - -- (UIImage *)imageCroppedToRect:(CGRect)rect -{ - //create drawing context - UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.0f); - - //draw - [self drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)]; - - //capture resultant image - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - //return image - return image; -} - -- (UIImage *)imageScaledToSize:(CGSize)size -{ - //avoid redundant drawing - if (CGSizeEqualToSize(self.size, size)) - { - return self; - } - - //create drawing context - UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f); - - //draw - [self drawInRect:CGRectMake(0.0f, 0.0f, size.width, size.height)]; - - //capture resultant image - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - //return image - return image; -} - -- (UIImage *)imageScaledToFitSize:(CGSize)size -{ - //calculate rect - CGFloat aspect = self.size.width / self.size.height; - if (size.width / aspect <= size.height) - { - return [self imageScaledToSize:CGSizeMake(size.width, size.width / aspect)]; - } - else - { - return [self imageScaledToSize:CGSizeMake(size.height * aspect, size.height)]; - } -} - -- (UIImage *)imageScaledToFillSize:(CGSize)size -{ - if (CGSizeEqualToSize(self.size, size)) - { - return self; - } - //calculate rect - CGFloat aspect = self.size.width / self.size.height; - if (size.width / aspect >= size.height) - { - return [self imageScaledToSize:CGSizeMake(size.width, size.width / aspect)]; - } - else - { - return [self imageScaledToSize:CGSizeMake(size.height * aspect, size.height)]; - } -} - -- (UIImage *)imageCroppedAndScaledToSize:(CGSize)size - contentMode:(UIViewContentMode)contentMode - padToFit:(BOOL)padToFit; -{ - //calculate rect - CGRect rect = CGRectZero; - switch (contentMode) - { - case UIViewContentModeScaleAspectFit: - { - CGFloat aspect = self.size.width / self.size.height; - if (size.width / aspect <= size.height) - { - rect = CGRectMake(0.0f, (size.height - size.width / aspect) / 2.0f, size.width, size.width / aspect); - } - else - { - rect = CGRectMake((size.width - size.height * aspect) / 2.0f, 0.0f, size.height * aspect, size.height); - } - break; - } - case UIViewContentModeScaleAspectFill: - { - CGFloat aspect = self.size.width / self.size.height; - if (size.width / aspect >= size.height) - { - rect = CGRectMake(0.0f, (size.height - size.width / aspect) / 2.0f, size.width, size.width / aspect); - } - else - { - rect = CGRectMake((size.width - size.height * aspect) / 2.0f, 0.0f, size.height * aspect, size.height); - } - break; - } - case UIViewContentModeCenter: - { - rect = CGRectMake((size.width - self.size.width) / 2.0f, (size.height - self.size.height) / 2.0f, self.size.width, self.size.height); - break; - } - case UIViewContentModeTop: - { - rect = CGRectMake((size.width - self.size.width) / 2.0f, 0.0f, self.size.width, self.size.height); - break; - } - case UIViewContentModeBottom: - { - rect = CGRectMake((size.width - self.size.width) / 2.0f, size.height - self.size.height, self.size.width, self.size.height); - break; - } - case UIViewContentModeLeft: - { - rect = CGRectMake(0.0f, (size.height - self.size.height) / 2.0f, self.size.width, self.size.height); - break; - } - case UIViewContentModeRight: - { - rect = CGRectMake(size.width - self.size.width, (size.height - self.size.height) / 2.0f, self.size.width, self.size.height); - break; - } - case UIViewContentModeTopLeft: - { - rect = CGRectMake(0.0f, 0.0f, self.size.width, self.size.height); - break; - } - case UIViewContentModeTopRight: - { - rect = CGRectMake(size.width - self.size.width, 0.0f, self.size.width, self.size.height); - break; - } - case UIViewContentModeBottomLeft: - { - rect = CGRectMake(0.0f, size.height - self.size.height, self.size.width, self.size.height); - break; - } - case UIViewContentModeBottomRight: - { - rect = CGRectMake(size.width - self.size.width, size.height - self.size.height, self.size.width, self.size.height); - break; - } - default: - { - rect = CGRectMake(0.0f, 0.0f, size.width, size.height); - break; - } - } - - if (!padToFit) - { - //remove padding - if (rect.size.width < size.width) - { - size.width = rect.size.width; - rect.origin.x = 0.0f; - } - if (rect.size.height < size.height) - { - size.height = rect.size.height; - rect.origin.y = 0.0f; - } - } - - //avoid redundant drawing - if (CGSizeEqualToSize(self.size, size)) - { - return self; - } - - //create drawing context - UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f); - - //draw - [self drawInRect:rect]; - - //capture resultant image - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - //return image - return image; -} - -+ (CGImageRef)gradientMask -{ - static CGImageRef sharedMask = NULL; - if (sharedMask == NULL) - { - //create gradient mask - UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 256), YES, 0.0); - CGContextRef gradientContext = UIGraphicsGetCurrentContext(); - CGFloat colors[] = {0.0, 1.0, 1.0, 1.0}; - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); - CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, 2); - CGPoint gradientStartPoint = CGPointMake(0, 0); - CGPoint gradientEndPoint = CGPointMake(0, 256); - CGContextDrawLinearGradient(gradientContext, gradient, gradientStartPoint, - gradientEndPoint, kCGGradientDrawsAfterEndLocation); - sharedMask = CGBitmapContextCreateImage(gradientContext); - CGGradientRelease(gradient); - CGColorSpaceRelease(colorSpace); - UIGraphicsEndImageContext(); - } - return sharedMask; -} - -- (UIImage *)reflectedImageWithScale:(CGFloat)scale -{ - //get reflection dimensions - CGFloat height = ceil(self.size.height * scale); - CGSize size = CGSizeMake(self.size.width, height); - CGRect bounds = CGRectMake(0.0f, 0.0f, size.width, size.height); - - //create drawing context - UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f); - CGContextRef context = UIGraphicsGetCurrentContext(); - - //clip to gradient - CGContextClipToMask(context, bounds, [[self class] gradientMask]); - - //draw reflected image - CGContextScaleCTM(context, 1.0f, -1.0f); - CGContextTranslateCTM(context, 0.0f, -self.size.height); - [self drawInRect:CGRectMake(0.0f, 0.0f, self.size.width, self.size.height)]; - - //capture resultant image - UIImage *reflection = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - //return reflection image - return reflection; -} - -- (UIImage *)imageWithReflectionWithScale:(CGFloat)scale gap:(CGFloat)gap alpha:(CGFloat)alpha -{ - //get reflected image - UIImage *reflection = [self reflectedImageWithScale:scale]; - CGFloat reflectionOffset = reflection.size.height + gap; - - //create drawing context - UIGraphicsBeginImageContextWithOptions(CGSizeMake(self.size.width, self.size.height + reflectionOffset * 2.0f), NO, 0.0f); - - //draw reflection - [reflection drawAtPoint:CGPointMake(0.0f, reflectionOffset + self.size.height + gap) blendMode:kCGBlendModeNormal alpha:alpha]; - - //draw image - [self drawAtPoint:CGPointMake(0.0f, reflectionOffset)]; - - //capture resultant image - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - //return image - return image; -} - -- (UIImage *)imageWithShadowColor:(UIColor *)color offset:(CGSize)offset blur:(CGFloat)blur -{ - //get size - //CGSize border = CGSizeMake(fabsf(offset.width) + blur, fabsf(offset.height) + blur); - CGSize border = CGSizeMake(fabs(offset.width) + blur, fabs(offset.height) + blur); - - CGSize size = CGSizeMake(self.size.width + border.width * 2.0f, self.size.height + border.height * 2.0f); - - //create drawing context - UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f); - CGContextRef context = UIGraphicsGetCurrentContext(); - - //set up shadow - CGContextSetShadowWithColor(context, offset, blur, color.CGColor); - - //draw with shadow - [self drawAtPoint:CGPointMake(border.width, border.height)]; - - //capture resultant image - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - //return image - return image; -} - -- (UIImage *)imageWithCornerRadius:(CGFloat)radius -{ - //create drawing context - UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f); - CGContextRef context = UIGraphicsGetCurrentContext(); - - //clip image - CGContextBeginPath(context); - CGContextMoveToPoint(context, 0.0f, radius); - CGContextAddLineToPoint(context, 0.0f, self.size.height - radius); - CGContextAddArc(context, radius, self.size.height - radius, radius, M_PI, M_PI / 2.0f, 1); - CGContextAddLineToPoint(context, self.size.width - radius, self.size.height); - CGContextAddArc(context, self.size.width - radius, self.size.height - radius, radius, M_PI / 2.0f, 0.0f, 1); - CGContextAddLineToPoint(context, self.size.width, radius); - CGContextAddArc(context, self.size.width - radius, radius, radius, 0.0f, -M_PI / 2.0f, 1); - CGContextAddLineToPoint(context, radius, 0.0f); - CGContextAddArc(context, radius, radius, radius, -M_PI / 2.0f, M_PI, 1); - CGContextClip(context); - - //draw image - [self drawAtPoint:CGPointZero]; - - //capture resultant image - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - //return image - return image; -} - -- (UIImage *)imageWithAlpha:(CGFloat)alpha -{ - //create drawing context - UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f); - - //draw with alpha - [self drawAtPoint:CGPointZero blendMode:kCGBlendModeNormal alpha:alpha]; - - //capture resultant image - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - //return image - return image; -} - -- (UIImage *)imageWithMask:(UIImage *)maskImage; -{ - //create drawing context - UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f); - CGContextRef context = UIGraphicsGetCurrentContext(); - - //apply mask - CGContextClipToMask(context, CGRectMake(0.0f, 0.0f, self.size.width, self.size.height), maskImage.CGImage); - - //draw image - [self drawAtPoint:CGPointZero]; - - //capture resultant image - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - //return image - return image; -} - -- (UIImage *)maskImageFromImageAlpha -{ - //get dimensions - NSInteger width = CGImageGetWidth(self.CGImage); - NSInteger height = CGImageGetHeight(self.CGImage); - - //create alpha image - NSInteger bytesPerRow = ((width + 3) / 4) * 4; - void *data = calloc(bytesPerRow * height, sizeof(unsigned char *)); - CGContextRef context = CGBitmapContextCreate(data, width, height, 8, bytesPerRow, NULL, kCGImageAlphaOnly); - CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), self.CGImage); - - //invert alpha pixels - for (int y = 0; y < height; y++) - { - for (int x = 0; x < width; x++) - { - NSInteger index = y * bytesPerRow + x; - ((unsigned char *)data)[index] = 255 - ((unsigned char *)data)[index]; - } - } - - //create mask image - CGImageRef maskRef = CGBitmapContextCreateImage(context); - CGContextRelease(context); - UIImage *mask = [UIImage imageWithCGImage:maskRef]; - CGImageRelease(maskRef); - free(data); - - //return image - return mask; -} - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+FileName.h b/camerademo/camerademo/demo/UIImage/UIImage+FileName.h deleted file mode 100755 index 6e881f3..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+FileName.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// UIImage+FileName.h -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 14/12/15. -// Copyright (c) 2014��� www.skyfox.org. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface UIImage (FileName) -/** - * @brief ������bundle��������������������������� - * - * @param name ��������� - * - * @return ������������������ - */ -+ (UIImage *)imageWithFileName:(NSString *)name; - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+FileName.m b/camerademo/camerademo/demo/UIImage/UIImage+FileName.m deleted file mode 100755 index 6a468ee..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+FileName.m +++ /dev/null @@ -1,57 +0,0 @@ -// -// UIImage+FileName.m -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 14/12/15. -// Copyright (c) 2014��� www.skyfox.org. All rights reserved. -// -#import "UIImage+FileName.h" - - -@implementation UIImage (FileName) -/** - * @brief ������bundle��������������������������� - * - * @param name ��������� - * - * @return ������������������ - */ -+ (UIImage *)imageWithFileName:(NSString *)name { - NSString *extension = @"png"; - - NSArray *components = [name componentsSeparatedByString:@"."]; - if ([components count] >= 2) { - NSUInteger lastIndex = components.count - 1; - extension = [components objectAtIndex:lastIndex]; - - name = [name substringToIndex:(name.length-(extension.length+1))]; - } - - // ���������Retina���������������������������������������Retina��������������������������������� - if ([UIScreen mainScreen].scale == 2.0) { - name = [name stringByAppendingString:@"@2x"]; - - NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:extension]; - if (path != nil) { - return [UIImage imageWithContentsOfFile:path]; - } - } - - if ([UIScreen mainScreen].scale == 3.0) { - name = [name stringByAppendingString:@"@3x"]; - - NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:extension]; - if (path != nil) { - return [UIImage imageWithContentsOfFile:path]; - } - } - - NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:extension]; - if (path) { - return [UIImage imageWithContentsOfFile:path]; - } - - return nil; -} - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+GIF.h b/camerademo/camerademo/demo/UIImage/UIImage+GIF.h deleted file mode 100755 index 084f424..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+GIF.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// UIImage+GIF.h -// LBGIFImage -// -// Created by Laurin Brandner on 06.01.12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface UIImage (GIF) - -+ (UIImage *)sd_animatedGIFNamed:(NSString *)name; - -+ (UIImage *)sd_animatedGIFWithData:(NSData *)data; - -- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size; - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+GIF.m b/camerademo/camerademo/demo/UIImage/UIImage+GIF.m deleted file mode 100755 index a703637..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+GIF.m +++ /dev/null @@ -1,158 +0,0 @@ -// -// UIImage+GIF.m -// LBGIFImage -// -// Created by Laurin Brandner on 06.01.12. -// Copyright (c) 2012 __MyCompanyName__. All rights reserved. -// - -#import "UIImage+GIF.h" -#import <ImageIO/ImageIO.h> - -@implementation UIImage (GIF) - -+ (UIImage *)sd_animatedGIFWithData:(NSData *)data { - if (!data) { - return nil; - } - - CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); - - size_t count = CGImageSourceGetCount(source); - - UIImage *animatedImage; - - if (count <= 1) { - animatedImage = [[UIImage alloc] initWithData:data]; - } - else { - NSMutableArray *images = [NSMutableArray array]; - - NSTimeInterval duration = 0.0f; - - for (size_t i = 0; i < count; i++) { - CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL); - - duration += [self sd_frameDurationAtIndex:i source:source]; - - [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]]; - - CGImageRelease(image); - } - - if (!duration) { - duration = (1.0f / 10.0f) * count; - } - - animatedImage = [UIImage animatedImageWithImages:images duration:duration]; - } - - CFRelease(source); - - return animatedImage; -} - -+ (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source { - float frameDuration = 0.1f; - CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil); - NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties; - NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; - - NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; - if (delayTimeUnclampedProp) { - frameDuration = [delayTimeUnclampedProp floatValue]; - } - else { - - NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; - if (delayTimeProp) { - frameDuration = [delayTimeProp floatValue]; - } - } - - // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. - // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify - // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082> - // for more information. - - if (frameDuration < 0.011f) { - frameDuration = 0.100f; - } - - CFRelease(cfFrameProperties); - return frameDuration; -} - -+ (UIImage *)sd_animatedGIFNamed:(NSString *)name { - CGFloat scale = [UIScreen mainScreen].scale; - - if (scale > 1.0f) { - NSString *retinaPath = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"gif"]; - - NSData *data = [NSData dataWithContentsOfFile:retinaPath]; - - if (data) { - return [UIImage sd_animatedGIFWithData:data]; - } - - NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"]; - - data = [NSData dataWithContentsOfFile:path]; - - if (data) { - return [UIImage sd_animatedGIFWithData:data]; - } - - return [UIImage imageNamed:name]; - } - else { - NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"]; - - NSData *data = [NSData dataWithContentsOfFile:path]; - - if (data) { - return [UIImage sd_animatedGIFWithData:data]; - } - - return [UIImage imageNamed:name]; - } -} - -- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size { - if (CGSizeEqualToSize(self.size, size) || CGSizeEqualToSize(size, CGSizeZero)) { - return self; - } - - CGSize scaledSize = size; - CGPoint thumbnailPoint = CGPointZero; - - CGFloat widthFactor = size.width / self.size.width; - CGFloat heightFactor = size.height / self.size.height; - CGFloat scaleFactor = (widthFactor > heightFactor) ? widthFactor : heightFactor; - scaledSize.width = self.size.width * scaleFactor; - scaledSize.height = self.size.height * scaleFactor; - - if (widthFactor > heightFactor) { - thumbnailPoint.y = (size.height - scaledSize.height) * 0.5; - } - else if (widthFactor < heightFactor) { - thumbnailPoint.x = (size.width - scaledSize.width) * 0.5; - } - - NSMutableArray *scaledImages = [NSMutableArray array]; - - UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); - - for (UIImage *image in self.images) { - [image drawInRect:CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledSize.width, scaledSize.height)]; - UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); - - [scaledImages addObject:newImage]; - } - - UIGraphicsEndImageContext(); - - return [UIImage animatedImageWithImages:scaledImages duration:self.duration]; -} - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Merge.h b/camerademo/camerademo/demo/UIImage/UIImage+Merge.h deleted file mode 100755 index a7eeb5c..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Merge.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// UIImage+Merge.h -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 14/12/30. -// Copyright (c) 2014��� www.skyfox.org. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface UIImage (Merge) -/** - * @brief ������������������ - * - * @param firstImage ������������ - * @param secondImage ������������ - * - * @return ��������������� - */ -+ (UIImage*)mergeImage:(UIImage*)firstImage withImage:(UIImage*)secondImage; -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Merge.m b/camerademo/camerademo/demo/UIImage/UIImage+Merge.m deleted file mode 100755 index b3dc743..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Merge.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// UIImage+Merge.m -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 14/12/30. -// Copyright (c) 2014��� www.skyfox.org. All rights reserved. -// - -#import "UIImage+Merge.h" - -@implementation UIImage (Merge) -/** - * @brief ������������������ - * - * @param firstImage ������������ - * @param secondImage ������������ - * - * @return ��������������� - */ -+ (UIImage*)mergeImage:(UIImage*)firstImage withImage:(UIImage*)secondImage { - CGImageRef firstImageRef = firstImage.CGImage; - CGFloat firstWidth = CGImageGetWidth(firstImageRef); - CGFloat firstHeight = CGImageGetHeight(firstImageRef); - CGImageRef secondImageRef = secondImage.CGImage; - CGFloat secondWidth = CGImageGetWidth(secondImageRef); - CGFloat secondHeight = CGImageGetHeight(secondImageRef); - CGSize mergedSize = CGSizeMake(MAX(firstWidth, secondWidth), MAX(firstHeight, secondHeight)); - UIGraphicsBeginImageContext(mergedSize); - [firstImage drawInRect:CGRectMake(0, 0, firstWidth, firstHeight)]; - [secondImage drawInRect:CGRectMake(0, 0, secondWidth, secondHeight)]; - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return image; -} -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Orientation.h b/camerademo/camerademo/demo/UIImage/UIImage+Orientation.h deleted file mode 100755 index 89a3fd9..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Orientation.h +++ /dev/null @@ -1,69 +0,0 @@ -// -// UIImage+Orientation.h -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 15/1/4. -// Copyright (c) 2015��� www.skyfox.org. All rights reserved. -// - -#import <UIKit/UIKit.h> -//CGFloat DegreesToRadiansForOrientation(CGFloat degrees) {return degrees * M_PI / 180;}; -//CGFloat RadiansToDegreesForOrientation(CGFloat radians) {return radians * 180/M_PI;}; -@interface UIImage (Orientation) -/** - * @brief ��������������������� - * - * @param srcImg ������ - * - * @return ������������������������ - */ -+ (UIImage *)fixOrientation:(UIImage *)srcImg; -/** - * @brief ������������ - * - * @param degrees ������ - * - * @return ��������������� - */ -- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees; - -/** - * @brief ������������ - * - * @param degrees ������ - * - * @return ��������������� - */ -- (UIImage *)imageRotatedByRadians:(CGFloat)radians; - -/** - * @brief ������������ - * - * @return ������������������ - */ -- (UIImage *)flipVertical; -/** - * @brief ������������ - * - * @return ������������������ - */ -- (UIImage *)flipHorizontal; - -/** - * @brief ��������������� - * - * @param degrees ������ - * - * @return ������ - */ -+(CGFloat)degreesToRadians:(CGFloat)degrees; -/** - * @brief ��������������� - * - * @param radians ������ - * - * @return ������ - */ -+(CGFloat)radiansToDegrees:(CGFloat)radians; - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Orientation.m b/camerademo/camerademo/demo/UIImage/UIImage+Orientation.m deleted file mode 100755 index 98a1a98..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Orientation.m +++ /dev/null @@ -1,181 +0,0 @@ -// -// UIImage+Orientation.m -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 15/1/4. -// Copyright (c) 2015��� www.skyfox.org. All rights reserved. -// - -#import "UIImage+Orientation.h" - -@implementation UIImage (Orientation) -/** - * @brief ��������������������� - * - * @param srcImg ������ - * - * @return ������������������������ - */ -+ (UIImage *)fixOrientation:(UIImage *)srcImg { - if (srcImg.imageOrientation == UIImageOrientationUp) return srcImg; - CGAffineTransform transform = CGAffineTransformIdentity; - switch (srcImg.imageOrientation) { - case UIImageOrientationDown: - case UIImageOrientationDownMirrored: - transform = CGAffineTransformTranslate(transform, srcImg.size.width, srcImg.size.height); - transform = CGAffineTransformRotate(transform, M_PI); - break; - case UIImageOrientationLeft: - case UIImageOrientationLeftMirrored: - transform = CGAffineTransformTranslate(transform, srcImg.size.width, 0); - transform = CGAffineTransformRotate(transform, M_PI_2); - break; - case UIImageOrientationRight: - case UIImageOrientationRightMirrored: - transform = CGAffineTransformTranslate(transform, 0, srcImg.size.height); - transform = CGAffineTransformRotate(transform, -M_PI_2); - break; - case UIImageOrientationUp: - case UIImageOrientationUpMirrored: - break; - } - switch (srcImg.imageOrientation) { - case UIImageOrientationUpMirrored: - case UIImageOrientationDownMirrored: - transform = CGAffineTransformTranslate(transform, srcImg.size.width, 0); - transform = CGAffineTransformScale(transform, -1, 1); - break; - case UIImageOrientationLeftMirrored: - case UIImageOrientationRightMirrored: - transform = CGAffineTransformTranslate(transform, srcImg.size.height, 0); - transform = CGAffineTransformScale(transform, -1, 1); - break; - case UIImageOrientationUp: - case UIImageOrientationDown: - case UIImageOrientationLeft: - case UIImageOrientationRight: - break; - } - CGContextRef ctx = CGBitmapContextCreate(NULL, srcImg.size.width, srcImg.size.height, - CGImageGetBitsPerComponent(srcImg.CGImage), 0, - CGImageGetColorSpace(srcImg.CGImage), - CGImageGetBitmapInfo(srcImg.CGImage)); - CGContextConcatCTM(ctx, transform); - switch (srcImg.imageOrientation) { - case UIImageOrientationLeft: - case UIImageOrientationLeftMirrored: - case UIImageOrientationRight: - case UIImageOrientationRightMirrored: - CGContextDrawImage(ctx, CGRectMake(0,0,srcImg.size.height,srcImg.size.width), srcImg.CGImage); - break; - default: - CGContextDrawImage(ctx, CGRectMake(0,0,srcImg.size.width,srcImg.size.height), srcImg.CGImage); - break; - } - CGImageRef cgimg = CGBitmapContextCreateImage(ctx); - UIImage *img = [UIImage imageWithCGImage:cgimg]; - CGContextRelease(ctx); - CGImageRelease(cgimg); - return img; -} - -- (UIImage *)flip:(BOOL)isHorizontal { - CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height); - UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0); - - CGContextRef ctx = UIGraphicsGetCurrentContext(); - CGContextClipToRect(ctx, rect); - if (isHorizontal) { - CGContextRotateCTM(ctx, M_PI); - CGContextTranslateCTM(ctx, -rect.size.width, -rect.size.height); - } - CGContextDrawImage(ctx, rect, self.CGImage); - UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return image; -} -/** - * @brief ������������ - * - * @return ������������������ - */ -- (UIImage *)flipVertical { - return [self flip:NO]; -} -/** - * @brief ������������ - * - * @return ������������������ - */ -- (UIImage *)flipHorizontal { - return [self flip:YES]; -} -/** - * @brief ������������ - * - * @param degrees ������ - * - * @return ��������������� - */ -- (UIImage *)imageRotatedByRadians:(CGFloat)radians -{ - return [self imageRotatedByDegrees:[UIImage radiansToDegrees:radians]]; -} -/** - * @brief ������������ - * - * @param degrees ��� - * - * @return ��������������� - */ -- (UIImage *)imageRotatedByDegrees:(CGFloat)degrees -{ - // calculate the size of the rotated view's containing box for our drawing space - UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.size.width, self.size.height)]; - CGAffineTransform t = CGAffineTransformMakeRotation([UIImage degreesToRadians:degrees]); - rotatedViewBox.transform = t; - CGSize rotatedSize = rotatedViewBox.frame.size; - - // Create the bitmap context - UIGraphicsBeginImageContext(rotatedSize); - CGContextRef bitmap = UIGraphicsGetCurrentContext(); - - // Move the origin to the middle of the image so we will rotate and scale around the center. - CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2); - - // // Rotate the image context - CGContextRotateCTM(bitmap, [UIImage degreesToRadians:degrees]); - - // Now, draw the rotated/scaled image into the context - CGContextScaleCTM(bitmap, 1.0, -1.0); - CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2, self.size.width, self.size.height), [self CGImage]); - - UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return newImage; - -} - -/** - * @brief ��������������� - * - * @param degrees ������ - * - * @return ������ - */ -+(CGFloat)degreesToRadians:(CGFloat)degrees -{ - return degrees * M_PI / 180; -} -/** - * @brief ��������������� - * - * @param radians ������ - * - * @return ������ - */ -+(CGFloat)radiansToDegrees:(CGFloat)radians -{ - return radians * 180/M_PI; -} -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+PDF.h b/camerademo/camerademo/demo/UIImage/UIImage+PDF.h deleted file mode 100755 index 27db567..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+PDF.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// UIImage+PDF.h -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 15/5/22. -// Copyright (c) 2015��� www.skyfox.org. All rights reserved. -// - -#import <UIKit/UIKit.h> - -@interface UIImage (PDF) - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+PDF.m b/camerademo/camerademo/demo/UIImage/UIImage+PDF.m deleted file mode 100755 index e6ca42f..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+PDF.m +++ /dev/null @@ -1,13 +0,0 @@ -// -// UIImage+PDF.m -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 15/5/22. -// Copyright (c) 2015��� www.skyfox.org. All rights reserved. -// - -#import "UIImage+PDF.h" - -@implementation UIImage (PDF) - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+RemoteSize.h b/camerademo/camerademo/demo/UIImage/UIImage+RemoteSize.h deleted file mode 100755 index 8d4d1f2..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+RemoteSize.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// UIImage+RemoteSize.h -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 15/1/27. -// Copyright (c) 2015��� www.skyfox.org. All rights reserved. -// - -#import <UIKit/UIKit.h> - -typedef void (^UIImageSizeRequestCompleted) (NSURL* imgURL, CGSize size); - -@interface UIImage (RemoteSize) -/** - * @brief ��������������������������� - * - * @param imgURL ������url - * @param completion ������������ - */ -+ (void)requestSizeNoHeader:(NSURL*)imgURL completion:(UIImageSizeRequestCompleted)completion; -/** - * @brief ���header������������������������������ (���������������������) - * - * @param imgURL ������url - * @param completion ������������ - */ -//+ (void)requestSizeWithHeader:(NSURL*)imgURL completion:(UIImageSizeRequestCompleted)completion; - -@end \ No newline at end of file diff --git a/camerademo/camerademo/demo/UIImage/UIImage+RemoteSize.m b/camerademo/camerademo/demo/UIImage/UIImage+RemoteSize.m deleted file mode 100755 index 82d4085..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+RemoteSize.m +++ /dev/null @@ -1,270 +0,0 @@ -// -// UIImage+RemoteSize.m -// iOS-Categories (https://github.com/shaojiankui/iOS-Categories) -// -// Created by Jakey on 15/1/27. -// Copyright (c) 2015��� www.skyfox.org. All rights reserved. -// - -#import "UIImage+RemoteSize.h" - -#import <objc/runtime.h> - -static char *kSizeRequestDataKey = "NSURL.sizeRequestData"; -static char *kSizeRequestTypeKey = "NSURL.sizeRequestType"; -static char *kSizeRequestCompletionKey = "NSURL.sizeRequestCompletion"; - -typedef uint32_t dword; - -@interface NSURL (RemoteSize) -@property (nonatomic, strong) NSMutableData* sizeRequestData; -@property (nonatomic, strong) NSString* sizeRequestType; -@property (nonatomic, copy) UIImageSizeRequestCompleted sizeRequestCompletion; -@end - -@implementation NSURL (RemoteSize) - -- (void)setSizeRequestCompletion: (UIImageSizeRequestCompleted) block { - objc_setAssociatedObject(self, &kSizeRequestCompletionKey, block, OBJC_ASSOCIATION_COPY); -} - -- (UIImageSizeRequestCompleted)sizeRequestCompletion { - return objc_getAssociatedObject(self, &kSizeRequestCompletionKey); -} - -- (void)setSizeRequestData:(NSMutableData *)sizeRequestData { - objc_setAssociatedObject(self, &kSizeRequestDataKey, sizeRequestData, OBJC_ASSOCIATION_RETAIN); -} - -- (NSMutableData*)sizeRequestData { - return objc_getAssociatedObject(self, &kSizeRequestDataKey); -} - -- (void)setSizeRequestType:(NSString *)sizeRequestType { - objc_setAssociatedObject(self, &kSizeRequestTypeKey, sizeRequestType, OBJC_ASSOCIATION_RETAIN); -} - -- (NSString*)sizeRequestType { - return objc_getAssociatedObject(self, &kSizeRequestTypeKey); -} - -#pragma mark - NSURLConnectionDelegate -- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse *)response { - [self.sizeRequestData setLength: 0]; //Redirected => reset data -} - -- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData *)data { - NSMutableData* receivedData = self.sizeRequestData; - - if( !receivedData ) { - receivedData = [NSMutableData data]; - self.sizeRequestData = receivedData; - } - - [receivedData appendData: data]; - - //Parse metadata - const unsigned char* cString = [receivedData bytes]; - const NSInteger length = [receivedData length]; - - const char pngSignature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; - const char bmpSignature[2] = {66, 77}; - const char gifSignature[2] = {71, 73}; - const char jpgSignature[2] = {255, 216}; - - if(!self.sizeRequestType ) { - if( memcmp(pngSignature, cString, 8) == 0 ) { - self.sizeRequestType = @"PNG"; - } - else if( memcmp(bmpSignature, cString, 2) == 0 ) { - self.sizeRequestType = @"BMP"; - } - else if( memcmp(jpgSignature, cString, 2) == 0 ) { - self.sizeRequestType = @"JPG"; - } - else if( memcmp(gifSignature, cString, 2) == 0 ) { - self.sizeRequestType = @"GIF"; - } - } - - if( [self.sizeRequestType isEqualToString: @"PNG"] ) { - char type[5]; - int offset = 8; - - dword chunkSize = 0; - int chunkSizeSize = sizeof(chunkSize); - - if( offset+chunkSizeSize > length ) - return; - - memcpy(&chunkSize, cString+offset, chunkSizeSize); - chunkSize = OSSwapInt32(chunkSize); - offset += chunkSizeSize; - - if( offset + chunkSize > length ) - return; - - memcpy(&type, cString+offset, 4); type[4]='\0'; - offset += 4; - - if( strcmp(type, "IHDR") == 0 ) { //Should always be first - dword width = 0, height = 0; - memcpy(&width, cString+offset, 4); - offset += 4; - width = OSSwapInt32(width); - - memcpy(&height, cString+offset, 4); - offset += 4; - height = OSSwapInt32(height); - - if( self.sizeRequestCompletion ) { - self.sizeRequestCompletion(self, CGSizeMake(width, height)); - } - - self.sizeRequestCompletion = nil; - - [connection cancel]; - } - } - else if( [self.sizeRequestType isEqualToString: @"BMP"] ) { - int offset = 18; - dword width = 0, height = 0; - memcpy(&width, cString+offset, 4); - offset += 4; - - memcpy(&height, cString+offset, 4); - offset += 4; - - if( self.sizeRequestCompletion ) { - self.sizeRequestCompletion(self, CGSizeMake(width, height)); - } - - self.sizeRequestCompletion = nil; - - [connection cancel]; - } - else if( [self.sizeRequestType isEqualToString: @"JPG"] ) { - int offset = 4; - dword block_length = cString[offset]*256 + cString[offset+1]; - - while (offset<length) { - offset += block_length; - - if( offset >= length ) - break; - if( cString[offset] != 0xFF ) - break; - if( cString[offset+1] == 0xC0 || - cString[offset+1] == 0xC1 || - cString[offset+1] == 0xC2 || - cString[offset+1] == 0xC3 || - cString[offset+1] == 0xC5 || - cString[offset+1] == 0xC6 || - cString[offset+1] == 0xC7 || - cString[offset+1] == 0xC9 || - cString[offset+1] == 0xCA || - cString[offset+1] == 0xCB || - cString[offset+1] == 0xCD || - cString[offset+1] == 0xCE || - cString[offset+1] == 0xCF ) { - - dword width = 0, height = 0; - - height = cString[offset+5]*256 + cString[offset+6]; - width = cString[offset+7]*256 + cString[offset+8]; - - if( self.sizeRequestCompletion ) { - self.sizeRequestCompletion(self, CGSizeMake(width, height)); - } - - self.sizeRequestCompletion = nil; - - [connection cancel]; - - } - else { - offset += 2; - block_length = cString[offset]*256 + cString[offset+1]; - } - - } - } - else if( [self.sizeRequestType isEqualToString: @"GIF"] ) { - int offset = 6; - dword width = 0, height = 0; - memcpy(&width, cString+offset, 2); - offset += 2; - - memcpy(&height, cString+offset, 2); - offset += 2; - - if( self.sizeRequestCompletion ) { - self.sizeRequestCompletion(self, CGSizeMake(width, height)); - } - - self.sizeRequestCompletion = nil; - - [connection cancel]; - } -} - --(void)connection:(NSURLConnection*)connection didFailWithError:(NSError *)error { - if( self.sizeRequestCompletion ) - self.sizeRequestCompletion(self, CGSizeZero); -} - --(NSCachedURLResponse*)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { - return cachedResponse; -} - -- (void)connectionDidFinishLoading: (NSURLConnection *)connection { - // Basically, we failed to obtain the image size using metadata and the - // entire image was downloaded... - - if(!self.sizeRequestData.length) { - self.sizeRequestData = nil; - } - else { - //Try parse to UIImage - UIImage* image = [UIImage imageWithData: self.sizeRequestData]; - - if( self.sizeRequestCompletion && image) { - self.sizeRequestCompletion(self, [image size]); - return; - } - } - - self.sizeRequestCompletion(self, CGSizeZero); -} - -@end - -@implementation UIImage (RemoteSize) - -+ (void)requestSizeNoHeader:(NSURL*)imgURL completion:(UIImageSizeRequestCompleted)completion{ - - if([imgURL isFileURL] ) { - //Load from file stream - } - else { - imgURL.sizeRequestCompletion = completion; - - NSURLRequest* request = [NSURLRequest requestWithURL:imgURL]; - NSURLConnection* conn = [NSURLConnection connectionWithRequest: request delegate: imgURL]; - [conn scheduleInRunLoop: [NSRunLoop mainRunLoop] forMode: NSDefaultRunLoopMode]; - [conn start]; - } -} - - -+ (void)requestSizeWithHeader:(NSURL*)imgURL completion:(UIImageSizeRequestCompleted)completion{ -// NSURLRequest* request = [NSURLRequest requestWithURL:imgURL]; -// -// [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *resp, NSData *d, NSError *e) { -// NSLog(@"respone%@", [(NSHTTPURLResponse*)resp allHeaderFields]); -// -// -// }]; -} - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Resize.h b/camerademo/camerademo/demo/UIImage/UIImage+Resize.h deleted file mode 100755 index 64610b1..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Resize.h +++ /dev/null @@ -1,21 +0,0 @@ -// UIImage+Resize.h -// Created by Trevor Harmon on 8/5/09. -// Free for personal or commercial use, with or without modification. -// No warranty is expressed or implied. - -// Extends the UIImage class to support resizing/cropping -#import <UIKit/UIKit.h> - -@interface UIImage (Resize) - -- (UIImage *)croppedImage:(CGRect)bounds; -- (UIImage *)thumbnailImage:(NSInteger)thumbnailSize - transparentBorder:(NSUInteger)borderSize - cornerRadius:(NSUInteger)cornerRadius - interpolationQuality:(CGInterpolationQuality)quality; -- (UIImage *)resizedImage:(CGSize)newSize - interpolationQuality:(CGInterpolationQuality)quality; -- (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode - bounds:(CGSize)bounds - interpolationQuality:(CGInterpolationQuality)quality; -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Resize.m b/camerademo/camerademo/demo/UIImage/UIImage+Resize.m deleted file mode 100755 index affbf31..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Resize.m +++ /dev/null @@ -1,200 +0,0 @@ -// UIImage+Resize.m -// Created by Trevor Harmon on 8/5/09. -// Free for personal or commercial use, with or without modification. -// No warranty is expressed or implied. - -#import "UIImage+Resize.h" -#import "UIImage+RoundedCorner.h" -#import "UIImage+Alpha.h" - -// Private helper methods -@interface UIImage (ResizePrivateMethods) -- (UIImage *)resizedImage:(CGSize)newSize - transform:(CGAffineTransform)transform - drawTransposed:(BOOL)transpose - interpolationQuality:(CGInterpolationQuality)quality; -- (CGAffineTransform)transformForOrientation:(CGSize)newSize; -@end - -@implementation UIImage (Resize) - -// Returns a copy of this image that is cropped to the given bounds. -// The bounds will be adjusted using CGRectIntegral. -// This method ignores the image's imageOrientation setting. -- (UIImage *)croppedImage:(CGRect)bounds { - CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], bounds); - UIImage *croppedImage = [UIImage imageWithCGImage:imageRef]; - CGImageRelease(imageRef); - return croppedImage; -} - -// Returns a copy of this image that is squared to the thumbnail size. -// If transparentBorder is non-zero, a transparent border of the given size will be added around the edges of the thumbnail. (Adding a transparent border of at least one pixel in size has the side-effect of antialiasing the edges of the image when rotating it using Core Animation.) -- (UIImage *)thumbnailImage:(NSInteger)thumbnailSize - transparentBorder:(NSUInteger)borderSize - cornerRadius:(NSUInteger)cornerRadius - interpolationQuality:(CGInterpolationQuality)quality { - UIImage *resizedImage = [self resizedImageWithContentMode:UIViewContentModeScaleAspectFill - bounds:CGSizeMake(thumbnailSize, thumbnailSize) - interpolationQuality:quality]; - - // Crop out any part of the image that's larger than the thumbnail size - // The cropped rect must be centered on the resized image - // Round the origin points so that the size isn't altered when CGRectIntegral is later invoked - CGRect cropRect = CGRectMake(round((resizedImage.size.width - thumbnailSize) / 2), - round((resizedImage.size.height - thumbnailSize) / 2), - thumbnailSize, - thumbnailSize); - UIImage *croppedImage = [resizedImage croppedImage:cropRect]; - - UIImage *transparentBorderImage = borderSize ? [croppedImage transparentBorderImage:borderSize] : croppedImage; - - return [transparentBorderImage roundedCornerImage:cornerRadius borderSize:borderSize]; -} - -// Returns a rescaled copy of the image, taking into account its orientation -// The image will be scaled disproportionately if necessary to fit the bounds specified by the parameter -- (UIImage *)resizedImage:(CGSize)newSize interpolationQuality:(CGInterpolationQuality)quality { - BOOL drawTransposed; - - switch (self.imageOrientation) { - case UIImageOrientationLeft: - case UIImageOrientationLeftMirrored: - case UIImageOrientationRight: - case UIImageOrientationRightMirrored: - drawTransposed = YES; - break; - - default: - drawTransposed = NO; - } - - return [self resizedImage:newSize - transform:[self transformForOrientation:newSize] - drawTransposed:drawTransposed - interpolationQuality:quality]; -} - -// Resizes the image according to the given content mode, taking into account the image's orientation -- (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode - bounds:(CGSize)bounds - interpolationQuality:(CGInterpolationQuality)quality { - CGFloat horizontalRatio = bounds.width / self.size.width; - CGFloat verticalRatio = bounds.height / self.size.height; - CGFloat ratio; - - switch (contentMode) { - case UIViewContentModeScaleAspectFill: - ratio = MAX(horizontalRatio, verticalRatio); - break; - - case UIViewContentModeScaleAspectFit: - ratio = MIN(horizontalRatio, verticalRatio); - break; - - default: - [NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %@", @(contentMode)]; - } - - CGSize newSize = CGSizeMake(round(self.size.width * ratio), round(self.size.height * ratio)); - - return [self resizedImage:newSize interpolationQuality:quality]; -} - -#pragma mark - -#pragma mark Private helper methods - -// Returns a copy of the image that has been transformed using the given affine transform and scaled to the new size -// The new image's orientation will be UIImageOrientationUp, regardless of the current image's orientation -// If the new size is not integral, it will be rounded up -- (UIImage *)resizedImage:(CGSize)newSize - transform:(CGAffineTransform)transform - drawTransposed:(BOOL)transpose - interpolationQuality:(CGInterpolationQuality)quality { - CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height)); - CGRect transposedRect = CGRectMake(0, 0, newRect.size.height, newRect.size.width); - CGImageRef imageRef = self.CGImage; - - // Build a context that's the same dimensions as the new size - uint32_t bitmapInfo = CGImageGetBitmapInfo(imageRef); - if((bitmapInfo == kCGImageAlphaLast) || (bitmapInfo == kCGImageAlphaNone)) - bitmapInfo = kCGImageAlphaNoneSkipLast; - - - - CGContextRef bitmap = CGBitmapContextCreate(NULL, - newRect.size.width, - newRect.size.height, - CGImageGetBitsPerComponent(imageRef), - 0, - CGImageGetColorSpace(imageRef), - bitmapInfo); - - // Rotate and/or flip the image if required by its orientation - CGContextConcatCTM(bitmap, transform); - - // Set the quality level to use when rescaling - CGContextSetInterpolationQuality(bitmap, quality); - - // Draw into the context; this scales the image - CGContextDrawImage(bitmap, transpose ? transposedRect : newRect, imageRef); - - // Get the resized image from the context and a UIImage - CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap); - UIImage *newImage = [UIImage imageWithCGImage:newImageRef]; - - // Clean up - CGContextRelease(bitmap); - CGImageRelease(newImageRef); - - return newImage; -} - -// Returns an affine transform that takes into account the image orientation when drawing a scaled image -- (CGAffineTransform)transformForOrientation:(CGSize)newSize { - CGAffineTransform transform = CGAffineTransformIdentity; - - switch (self.imageOrientation) { - case UIImageOrientationDown: // EXIF = 3 - case UIImageOrientationDownMirrored: // EXIF = 4 - transform = CGAffineTransformTranslate(transform, newSize.width, newSize.height); - transform = CGAffineTransformRotate(transform, M_PI); - break; - - case UIImageOrientationLeft: // EXIF = 6 - case UIImageOrientationLeftMirrored: // EXIF = 5 - transform = CGAffineTransformTranslate(transform, newSize.width, 0); - transform = CGAffineTransformRotate(transform, M_PI_2); - break; - - case UIImageOrientationRight: // EXIF = 8 - case UIImageOrientationRightMirrored: // EXIF = 7 - transform = CGAffineTransformTranslate(transform, 0, newSize.height); - transform = CGAffineTransformRotate(transform, -M_PI_2); - break; - - default: - break; - } - - switch (self.imageOrientation) { - case UIImageOrientationUpMirrored: // EXIF = 2 - case UIImageOrientationDownMirrored: // EXIF = 4 - transform = CGAffineTransformTranslate(transform, newSize.width, 0); - transform = CGAffineTransformScale(transform, -1, 1); - break; - - case UIImageOrientationLeftMirrored: // EXIF = 5 - case UIImageOrientationRightMirrored: // EXIF = 7 - transform = CGAffineTransformTranslate(transform, newSize.height, 0); - transform = CGAffineTransformScale(transform, -1, 1); - break; - - default: - break; - } - - return transform; -} - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+RoundedCorner.h b/camerademo/camerademo/demo/UIImage/UIImage+RoundedCorner.h deleted file mode 100755 index f9d9803..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+RoundedCorner.h +++ /dev/null @@ -1,10 +0,0 @@ -// UIImage+RoundedCorner.h -// Created by Trevor Harmon on 9/20/09. -// Free for personal or commercial use, with or without modification. -// No warranty is expressed or implied. - -// Extends the UIImage class to support making rounded corners -#import <UIKit/UIKit.h> -@interface UIImage (RoundedCorner) -- (UIImage *)roundedCornerImage:(NSInteger)cornerSize borderSize:(NSInteger)borderSize; -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+RoundedCorner.m b/camerademo/camerademo/demo/UIImage/UIImage+RoundedCorner.m deleted file mode 100755 index f917f2c..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+RoundedCorner.m +++ /dev/null @@ -1,79 +0,0 @@ -// UIImage+RoundedCorner.m -// Created by Trevor Harmon on 9/20/09. -// Free for personal or commercial use, with or without modification. -// No warranty is expressed or implied. - -#import "UIImage+RoundedCorner.h" -#import "UIImage+Alpha.h" - -// Private helper methods -@interface UIImage (RoundedCornerPrivateMethods) -- (void)addRoundedRectToPath:(CGRect)rect context:(CGContextRef)context ovalWidth:(CGFloat)ovalWidth ovalHeight:(CGFloat)ovalHeight; -@end - -@implementation UIImage (RoundedCorner) - -// Creates a copy of this image with rounded corners -// If borderSize is non-zero, a transparent border of the given size will also be added -// Original author: Bj��rn S��llarp. Used with permission. See: http://blog.sallarp.com/iphone-uiimage-round-corners/ -- (UIImage *)roundedCornerImage:(NSInteger)cornerSize borderSize:(NSInteger)borderSize { - // If the image does not have an alpha layer, add one - UIImage *image = [self imageWithAlpha]; - - // Build a context that's the same dimensions as the new size - CGContextRef context = CGBitmapContextCreate(NULL, - image.size.width, - image.size.height, - CGImageGetBitsPerComponent(image.CGImage), - 0, - CGImageGetColorSpace(image.CGImage), - CGImageGetBitmapInfo(image.CGImage)); - - // Create a clipping path with rounded corners - CGContextBeginPath(context); - [self addRoundedRectToPath:CGRectMake(borderSize, borderSize, image.size.width - borderSize * 2, image.size.height - borderSize * 2) - context:context - ovalWidth:cornerSize - ovalHeight:cornerSize]; - CGContextClosePath(context); - CGContextClip(context); - - // Draw the image to the context; the clipping path will make anything outside the rounded rect transparent - CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage); - - // Create a CGImage from the context - CGImageRef clippedImage = CGBitmapContextCreateImage(context); - CGContextRelease(context); - - // Create a UIImage from the CGImage - UIImage *roundedImage = [UIImage imageWithCGImage:clippedImage]; - CGImageRelease(clippedImage); - - return roundedImage; -} - -#pragma mark - -#pragma mark Private helper methods - -// Adds a rectangular path to the given context and rounds its corners by the given extents -// Original author: Bj��rn S��llarp. Used with permission. See: http://blog.sallarp.com/iphone-uiimage-round-corners/ -- (void)addRoundedRectToPath:(CGRect)rect context:(CGContextRef)context ovalWidth:(CGFloat)ovalWidth ovalHeight:(CGFloat)ovalHeight { - if (ovalWidth == 0 || ovalHeight == 0) { - CGContextAddRect(context, rect); - return; - } - CGContextSaveGState(context); - CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect)); - CGContextScaleCTM(context, ovalWidth, ovalHeight); - CGFloat fw = CGRectGetWidth(rect) / ovalWidth; - CGFloat fh = CGRectGetHeight(rect) / ovalHeight; - CGContextMoveToPoint(context, fw, fh/2); - CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1); - CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1); - CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1); - CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); - CGContextClosePath(context); - CGContextRestoreGState(context); -} - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Vector.h b/camerademo/camerademo/demo/UIImage/UIImage+Vector.h deleted file mode 100755 index 9f93d41..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Vector.h +++ /dev/null @@ -1,65 +0,0 @@ -// -// UIImage+Vector.h -// UIImage+Vector -// -// Created by David Keegan on 8/7/13. -// Copyright (c) 2013 David Keegan All rights reserved. -// - - -/** - * @Author(������) David Keegan - * - * @URL(������) https://github.com/kgn/UIImage-Vector - * - * @Version(������) 20150620 - * - * @Requirements(������������) - * - * @Description(������) UIImage category for dealing with vector formats like PDF and icon fonts. - * @Usage(������) .. - */ - -#import <UIKit/UIKit.h> - -@interface UIImage(Vector) - -/** - Create a UIImage from an icon font. - @param font The icon font. - @param iconNamed The name of the icon in the font. - @param tintColor The tint color to use for the icon. Defaults to black. - @param clipToBounds If YES the image will be clipped to the pixel bounds of the icon. - @param fontSize The font size to draw the icon at. - @return The resulting image. - */ -+ (UIImage *)iconWithFont:(UIFont *)font named:(NSString *)iconNamed - withTintColor:(UIColor *)tintColor clipToBounds:(BOOL)clipToBounds forSize:(CGFloat)fontSize; - -/** - Create a UIImage from a PDF icon. - @param pdfNamed The name of the PDF file in the application's resources directory. - @param height The height of the resulting image, the width will be based on the aspect ratio of the PDF. - @return The resulting image. - */ -+ (UIImage *)imageWithPDFNamed:(NSString *)pdfNamed forHeight:(CGFloat)height; - -/** - Create a UIImage from a PDF icon. - @param pdfNamed The name of the PDF file in the application's resources directory. - @param tintColor The tint color to use for the icon. If nil no tint color will be used. - @param height The height of the resulting image, the width will be based on the aspect ratio of the PDF. - @return The resulting image. - */ -+ (UIImage *)imageWithPDFNamed:(NSString *)pdfNamed withTintColor:(UIColor *)tintColor forHeight:(CGFloat)height; - -/** - Create a UIImage from a PDF icon. - @param pdfFile The path of the PDF file. - @param tintColor The tint color to use for the icon. If nil no tint color will be used. - @param maxSize The maximum size the resulting image can be. The image will maintain it's aspect ratio and may not encumpas the full size. - @return The resulting image. - */ -+ (UIImage *)imageWithPDFFile:(NSString *)pdfFile withTintColor:(UIColor *)tintColor forSize:(CGSize)size; - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+Vector.m b/camerademo/camerademo/demo/UIImage/UIImage+Vector.m deleted file mode 100755 index e2d4af0..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+Vector.m +++ /dev/null @@ -1,140 +0,0 @@ -// -// UIImage+Vector.m -// UIImage+Vector -// -// Created by David Keegan on 8/7/13. -// Copyright (c) 2013 David Keegan All rights reserved. -// - -#import "UIImage+Vector.h" -#import <CoreText/CoreText.h> - -@implementation UIImage(Vector) - -+ (NSCache *)cache{ - static NSCache *cache = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - cache = [[NSCache alloc] init]; - }); - return cache; -} - -+ (UIImage *)iconWithFont:(UIFont *)font named:(NSString *)iconNamed withTintColor:(UIColor *)tintColor clipToBounds:(BOOL)clipToBounds forSize:(CGFloat)fontSize{ - NSString *identifier = [NSString stringWithFormat:@"%@%@%@%@%d%f", NSStringFromSelector(_cmd), font.fontName, tintColor, iconNamed, clipToBounds, fontSize]; - UIImage *image = [[self cache] objectForKey:identifier]; - if(image == nil){ - NSMutableAttributedString *ligature = [[NSMutableAttributedString alloc] initWithString:iconNamed]; - [ligature setAttributes:@{(NSString *)kCTLigatureAttributeName: @(2), - (NSString *)kCTFontAttributeName: font} - range:NSMakeRange(0, [ligature length])]; - - CGSize imageSize = [ligature size]; - imageSize.width = ceil(imageSize.width); - imageSize.height = ceil(imageSize.height); - if(!CGSizeEqualToSize(CGSizeZero, imageSize)){ - UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0); - [ligature drawAtPoint:CGPointZero]; - image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - if(tintColor){ - UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0); - CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextScaleCTM(context, 1, -1); - CGContextTranslateCTM(context, 0, -imageSize.height); - CGContextClipToMask(context, (CGRect){.size=imageSize}, [image CGImage]); - [tintColor setFill]; - CGContextFillRect(context, (CGRect){.size=imageSize}); - image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - } - - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wundeclared-selector" - if(clipToBounds && [image respondsToSelector:@selector(imageClippedToPixelBounds)]){ - image = [image performSelector:@selector(imageClippedToPixelBounds)]; - } - #pragma clang diagnostic pop - - [[self cache] setObject:image forKey:identifier]; - } - } - return image; -} - -+ (UIImage *)imageWithPDFNamed:(NSString *)pdfNamed forHeight:(CGFloat)height{ - return [self imageWithPDFNamed:pdfNamed withTintColor:nil forHeight:height]; -} - -+ (UIImage *)imageWithPDFNamed:(NSString *)pdfNamed withTintColor:(UIColor *)tintColor forHeight:(CGFloat)height{ - NSString *pdfFile = [[NSBundle mainBundle] pathForResource:pdfNamed ofType:@"pdf"]; - return [self imageWithPDFFile:pdfFile withTintColor:tintColor forSize:CGSizeMake(MAXFLOAT, height)]; -} - -+ (UIImage *)imageWithPDFFile:(NSString *)pdfFile withTintColor:(UIColor *)tintColor forSize:(CGSize)size{ - if(!pdfFile || CGSizeEqualToSize(size, CGSizeZero)){ - return nil; - } - - NSString *identifier = [NSString stringWithFormat:@"%@%@%@%@", NSStringFromSelector(_cmd), pdfFile, tintColor, NSStringFromCGSize(size)]; - UIImage *image = [[self cache] objectForKey:identifier]; - if(image){ - return image; - } - - NSURL *url = [NSURL fileURLWithPath:pdfFile]; - CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((__bridge CFURLRef)url); - if(!pdf){ - return nil; - } - - CGPDFPageRef page1 = CGPDFDocumentGetPage(pdf, 1); - CGRect mediaRect = CGPDFPageGetBoxRect(page1, kCGPDFCropBox); - - CGSize imageSize = mediaRect.size; - if(imageSize.height < size.height && size.height != MAXFLOAT){ - imageSize.width = round(size.height/imageSize.height*imageSize.width); - imageSize.height = size.height; - } - if(imageSize.width < size.width && size.width != MAXFLOAT){ - imageSize.height = round(size.width/imageSize.width*imageSize.height); - imageSize.width = size.width; - } - - if(imageSize.height > size.height){ - imageSize.width = round(size.height/imageSize.height*imageSize.width); - imageSize.height = size.height; - } - if(imageSize.width > size.width){ - imageSize.height = round(size.width/imageSize.width*imageSize.height); - imageSize.width = size.width; - } - - UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0); - CGFloat scale = MIN(imageSize.width/mediaRect.size.width, imageSize.height/mediaRect.size.height); - CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextScaleCTM(context, 1, -1); - CGContextTranslateCTM(context, 0, -imageSize.height); - CGContextScaleCTM(context, scale, scale); - CGContextDrawPDFPage(context, page1); - CGPDFDocumentRelease(pdf); - image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - - if(tintColor){ - UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0); - CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextScaleCTM(context, 1, -1); - CGContextTranslateCTM(context, 0, -imageSize.height); - CGContextClipToMask(context, (CGRect){.size=imageSize}, [image CGImage]); - [tintColor setFill]; - CGContextFillRect(context, (CGRect){.size=imageSize}); - image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - } - - return image; -} - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+animatedGIF.h b/camerademo/camerademo/demo/UIImage/UIImage+animatedGIF.h deleted file mode 100755 index 324d814..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+animatedGIF.h +++ /dev/null @@ -1,32 +0,0 @@ -#import <UIKit/UIKit.h> - -/** - UIImage (animatedGIF) - - This category adds class methods to `UIImage` to create an animated `UIImage` from an animated GIF. -*/ -@interface UIImage (animatedGIF) - -/* - UIImage *animation = [UIImage animatedImageWithAnimatedGIFData:theData]; - - I interpret `theData` as a GIF. I create an animated `UIImage` using the source images in the GIF. - - The GIF stores a separate duration for each frame, in units of centiseconds (hundredths of a second). However, a `UIImage` only has a single, total `duration` property, which is a floating-point number. - - To handle this mismatch, I add each source image (from the GIF) to `animation` a varying number of times to match the ratios between the frame durations in the GIF. - - For example, suppose the GIF contains three frames. Frame 0 has duration 3. Frame 1 has duration 9. Frame 2 has duration 15. I divide each duration by the greatest common denominator of all the durations, which is 3, and add each frame the resulting number of times. Thus `animation` will contain frame 0 3/3 = 1 time, then frame 1 9/3 = 3 times, then frame 2 15/3 = 5 times. I set `animation.duration` to (3+9+15)/100 = 0.27 seconds. -*/ -+ (UIImage *)animatedImageWithAnimatedGIFData:(NSData *)theData; - -/* - UIImage *image = [UIImage animatedImageWithAnimatedGIFURL:theURL]; - - I interpret the contents of `theURL` as a GIF. I create an animated `UIImage` using the source images in the GIF. - - I operate exactly like `+[UIImage animatedImageWithAnimatedGIFData:]`, except that I read the data from `theURL`. If `theURL` is not a `file:` URL, you probably want to call me on a background thread or GCD queue to avoid blocking the main thread. -*/ -+ (UIImage *)animatedImageWithAnimatedGIFURL:(NSURL *)theURL; - -@end diff --git a/camerademo/camerademo/demo/UIImage/UIImage+animatedGIF.m b/camerademo/camerademo/demo/UIImage/UIImage+animatedGIF.m deleted file mode 100755 index f6bdf5f..0000000 --- a/camerademo/camerademo/demo/UIImage/UIImage+animatedGIF.m +++ /dev/null @@ -1,114 +0,0 @@ -#import "UIImage+animatedGIF.h" -#import <ImageIO/ImageIO.h> - -#if __has_feature(objc_arc) -#define toCF (__bridge CFTypeRef) -#define fromCF (__bridge id) -#else -#define toCF (CFTypeRef) -#define fromCF (id) -#endif - -@implementation UIImage (animatedGIF) - -static int delayCentisecondsForImageAtIndex(CGImageSourceRef const source, size_t const i) { - int delayCentiseconds = 1; - CFDictionaryRef const properties = CGImageSourceCopyPropertiesAtIndex(source, i, NULL); - if (properties) { - CFDictionaryRef const gifProperties = CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary); - CFRelease(properties); - if (gifProperties) { - CFNumberRef const number = CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFDelayTime); - // Even though the GIF stores the delay as an integer number of centiseconds, ImageIO ���helpfully��� converts that to seconds for us. - delayCentiseconds = (int)lrint([fromCF number doubleValue] * 100); - } - } - return delayCentiseconds; -} - -static void createImagesAndDelays(CGImageSourceRef source, size_t count, CGImageRef imagesOut[count], int delayCentisecondsOut[count]) { - for (size_t i = 0; i < count; ++i) { - imagesOut[i] = CGImageSourceCreateImageAtIndex(source, i, NULL); - delayCentisecondsOut[i] = delayCentisecondsForImageAtIndex(source, i); - } -} - -static int sum(size_t const count, int const *const values) { - int theSum = 0; - for (size_t i = 0; i < count; ++i) { - theSum += values[i]; - } - return theSum; -} - -static int pairGCD(int a, int b) { - if (a < b) - return pairGCD(b, a); - while (true) { - int const r = a % b; - if (r == 0) - return b; - a = b; - b = r; - } -} - -static int vectorGCD(size_t const count, int const *const values) { - int gcd = values[0]; - for (size_t i = 1; i < count; ++i) { - // Note that after I process the first few elements of the vector, `gcd` will probably be smaller than any remaining element. By passing the smaller value as the second argument to `pairGCD`, I avoid making it swap the arguments. - gcd = pairGCD(values[i], gcd); - } - return gcd; -} - -static NSArray *frameArray(size_t const count, CGImageRef const images[count], int const delayCentiseconds[count], int const totalDurationCentiseconds) { - int const gcd = vectorGCD(count, delayCentiseconds); - size_t const frameCount = totalDurationCentiseconds / gcd; - UIImage *frames[frameCount]; - for (size_t i = 0, f = 0; i < count; ++i) { - UIImage *const frame = [UIImage imageWithCGImage:images[i]]; - for (size_t j = delayCentiseconds[i] / gcd; j > 0; --j) { - frames[f++] = frame; - } - } - return [NSArray arrayWithObjects:frames count:frameCount]; -} - -static void releaseImages(size_t const count, CGImageRef const images[count]) { - for (size_t i = 0; i < count; ++i) { - CGImageRelease(images[i]); - } -} - -static UIImage *animatedImageWithAnimatedGIFImageSource(CGImageSourceRef const source) { - size_t const count = CGImageSourceGetCount(source); - CGImageRef images[count]; - int delayCentiseconds[count]; // in centiseconds - createImagesAndDelays(source, count, images, delayCentiseconds); - int const totalDurationCentiseconds = sum(count, delayCentiseconds); - NSArray *const frames = frameArray(count, images, delayCentiseconds, totalDurationCentiseconds); - UIImage *const animation = [UIImage animatedImageWithImages:frames duration:(NSTimeInterval)totalDurationCentiseconds / 100.0]; - releaseImages(count, images); - return animation; -} - -static UIImage *animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceRef source) { - if (source) { - UIImage *const image = animatedImageWithAnimatedGIFImageSource(source); - CFRelease(source); - return image; - } else { - return nil; - } -} - -+ (UIImage *)animatedImageWithAnimatedGIFData:(NSData *)data { - return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithData(toCF data, NULL)); -} - -+ (UIImage *)animatedImageWithAnimatedGIFURL:(NSURL *)url { - return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithURL(toCF url, NULL)); -} - -@end -- Gitblit v1.8.0