├── CocoaPods-version.yml ├── Default-568h@2x.png ├── PNChartDemo ├── en.lproj │ └── InfoPlist.strings ├── PCChartsTableViewController.h ├── PCAppDelegate.h ├── main.m ├── PNChartDemo-Prefix.pch ├── Images.xcassets │ ├── LaunchImage.launchimage │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── PNChartDemo-Info.plist ├── PCChartViewController.h ├── PCChartsTableViewController.m ├── PCAppDelegate.m ├── Launch Screen.storyboard └── PCChartViewController.m ├── Podfile ├── PNChartDemo.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── PNChart.xcscheme ├── .travis.yml ├── PNChart ├── PNChartLabel.h ├── PNRadarChartDataItem.h ├── PNScatterChartDataItem.h ├── PNLineChartDataItem.h ├── PNScatterChartData.m ├── PNColor.m ├── PNPieChartDataItem.h ├── PNRadarChartDataItem.m ├── Info.plist ├── PNChartLabel.m ├── PNScatterChartDataItem.m ├── PNChart.h ├── PNChartDelegate.h ├── PNLineChartDataItem.m ├── PNPieChartDataItem.m ├── PNBar.h ├── PNScatterChartData.h ├── PNGenericChart.m ├── PNGenericChart.h ├── PNLineChartData.m ├── PNRadarChart.h ├── PNLineChartData.h ├── PNPieChart.h ├── PNScatterChart.h ├── PNCircleChart.h ├── PNColor.h ├── PNLineChart.h ├── PNBarChart.h ├── PNBar.m ├── PNCircleChart.m ├── PNRadarChart.m ├── PNBarChart.m ├── PNScatterChart.m └── PNPieChart.m ├── PNChartDemo.xcworkspace └── contents.xcworkspacedata ├── Podfile.lock ├── PNChartTests ├── Info.plist └── PNBarChartTests.m ├── LICENSE ├── UICountingLabel.h ├── .gitignore ├── PNChart.podspec ├── UICountingLabel.m └── README.md /CocoaPods-version.yml: -------------------------------------------------------------------------------- 1 | --- 2 | min: 0.32.1 3 | last: 0.32.1 4 | -------------------------------------------------------------------------------- /Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinzhow/PNChart/HEAD/Default-568h@2x.png -------------------------------------------------------------------------------- /PNChartDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | 3 | platform :ios, '8.0' 4 | 5 | target :PNChartDemo do 6 | pod 'UICountingLabel','~> 1.4.1' 7 | end 8 | 9 | target :PNChartTests do 10 | pod 'Expecta' 11 | end 12 | -------------------------------------------------------------------------------- /PNChartDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode9.1 3 | before_install: 4 | - gem install cocoapods -v '1.3.1' --no-rdoc --no-ri --no-document --quiet 5 | - pod install 6 | before_script: 7 | - export LANG=en_US.UTF-8 8 | xcode_workspace: PNChartDemo.xcworkspace 9 | -------------------------------------------------------------------------------- /PNChart/PNChartLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNChartLabel.h 3 | // PNChart 4 | // 5 | // Created by kevin on 10/3/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PNChartLabel : UILabel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PNChartDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /PNChartDemo/PCChartsTableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PCChartsTableViewController.h 3 | // PNChartDemo 4 | // 5 | // Created by kevinzhow on 13-12-1. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PCChartsTableViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /PNChartDemo/PCAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // PCAppDelegate.h 3 | // PNChartDemo 4 | // 5 | // Created by kevin on 11/7/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PCAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /PNChartDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PNChartDemo 4 | // 5 | // Created by kevin on 11/7/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PCAppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) 12 | { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([PCAppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Expecta (1.0.6) 3 | - UICountingLabel (1.4.1) 4 | 5 | DEPENDENCIES: 6 | - Expecta 7 | - UICountingLabel (~> 1.4.1) 8 | 9 | SPEC CHECKSUMS: 10 | Expecta: 3b6bd90a64b9a1dcb0b70aa0e10a7f8f631667d5 11 | UICountingLabel: faf890b505d96312e324a86718f031fafffb0ccb 12 | 13 | PODFILE CHECKSUM: de6be598c24bcfa6bcb5d22972adde29af718156 14 | 15 | COCOAPODS: 1.4.0 16 | -------------------------------------------------------------------------------- /PNChartDemo/PNChartDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /PNChart/PNRadarChartDataItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNRadarChartDataItem.h 3 | // PNChartDemo 4 | // 5 | // Created by Lei on 15/7/1. 6 | // Copyright (c) 2015年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | @interface PNRadarChartDataItem : NSObject 12 | 13 | + (instancetype)dataItemWithValue:(CGFloat)value 14 | description:(NSString *)description; 15 | 16 | @property (nonatomic) CGFloat value; 17 | @property (nonatomic,copy) NSString *textDescription; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /PNChart/PNScatterChartDataItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNScatterChartDataItem.h 3 | // PNChartDemo 4 | // 5 | // Created by Alireza Arabi on 12/4/14. 6 | // Copyright (c) 2014 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface PNScatterChartDataItem : NSObject 13 | 14 | + (PNScatterChartDataItem *)dataItemWithX:(CGFloat)x AndWithY:(CGFloat)y; 15 | 16 | @property (readonly) CGFloat x; // should be within the x range 17 | @property (readonly) CGFloat y; // should be within the y range 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /PNChart/PNLineChartDataItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jörg Polakowski on 14/12/13. 3 | // Copyright (c) 2013 kevinzhow. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | 9 | @interface PNLineChartDataItem : NSObject 10 | 11 | + (PNLineChartDataItem *)dataItemWithY:(CGFloat)y; 12 | + (PNLineChartDataItem *)dataItemWithY:(CGFloat)y andRawY:(CGFloat)rawY; 13 | 14 | @property (readonly) CGFloat y; // should be within the y range 15 | @property (readonly) CGFloat rawY; // this is the raw value, used for point label. 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /PNChartDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /PNChart/PNScatterChartData.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNScatterChartData.m 3 | // PNChartDemo 4 | // 5 | // Created by Alireza Arabi on 12/4/14. 6 | // Copyright (c) 2014 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNScatterChartData.h" 10 | 11 | @implementation PNScatterChartData 12 | 13 | - (id)init 14 | { 15 | self = [super init]; 16 | if (self) { 17 | [self setupDefaultValues]; 18 | } 19 | 20 | return self; 21 | } 22 | 23 | - (void)setupDefaultValues 24 | { 25 | _inflexionPointStyle = PNScatterChartPointStyleCircle; 26 | _fillColor = [UIColor grayColor]; 27 | _strokeColor = [UIColor clearColor]; 28 | _size = 3 ; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /PNChart/PNColor.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNColor.m 3 | // PNChart 4 | // 5 | // Created by kevin on 13-6-8. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNColor.h" 10 | #import 11 | 12 | @implementation PNColor 13 | 14 | - (UIImage *)imageFromColor:(UIColor *)color 15 | { 16 | CGRect rect = CGRectMake(0, 0, 1, 1); 17 | 18 | UIGraphicsBeginImageContext(rect.size); 19 | CGContextRef context = UIGraphicsGetCurrentContext(); 20 | CGContextSetFillColorWithColor(context, [color CGColor]); 21 | CGContextFillRect(context, rect); 22 | UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); 23 | UIGraphicsEndImageContext(); 24 | 25 | return img; 26 | } 27 | 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /PNChart/PNPieChartDataItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNPieChartDataItem.h 3 | // PNChartDemo 4 | // 5 | // Created by Hang Zhang on 14-5-5. 6 | // Copyright (c) 2014年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface PNPieChartDataItem : NSObject 13 | 14 | + (instancetype)dataItemWithValue:(CGFloat)value 15 | color:(UIColor*)color; 16 | 17 | + (instancetype)dataItemWithValue:(CGFloat)value 18 | color:(UIColor*)color 19 | description:(NSString *)description; 20 | 21 | @property (nonatomic) CGFloat value; 22 | @property (nonatomic) UIColor *color; 23 | @property (nonatomic) NSString *textDescription; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /PNChart/PNRadarChartDataItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNRadarChartDataItem.m 3 | // PNChartDemo 4 | // 5 | // Created by Lei on 15/7/1. 6 | // Copyright (c) 2015年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNRadarChartDataItem.h" 10 | 11 | @implementation PNRadarChartDataItem 12 | 13 | + (instancetype)dataItemWithValue:(CGFloat)value 14 | description:(NSString *)description { 15 | PNRadarChartDataItem *item = [PNRadarChartDataItem new]; 16 | item.value = value; 17 | item.textDescription = description; 18 | return item; 19 | } 20 | 21 | - (void)setValue:(CGFloat)value { 22 | if (value < 0) { 23 | value = 0; 24 | NSLog(@"Value value can not be negative"); 25 | } 26 | _value = value; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /PNChartTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /PNChart/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /PNChart/PNChartLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNChartLabel.m 3 | // PNChart 4 | // 5 | // Created by kevin on 10/3/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNChartLabel.h" 10 | 11 | @implementation PNChartLabel 12 | 13 | - (id)initWithFrame:(CGRect)frame 14 | { 15 | self = [super initWithFrame:frame]; 16 | 17 | if (self) { 18 | self.font = [UIFont boldSystemFontOfSize:11.0f]; 19 | self.backgroundColor = [UIColor clearColor]; 20 | self.textAlignment = NSTextAlignmentCenter; 21 | self.userInteractionEnabled = YES; 22 | self.adjustsFontSizeToFitWidth = YES; 23 | self.numberOfLines = 0; 24 | /* if you want to see ... in large labels un-comment this line 25 | self.minimumScaleFactor = 0.8; 26 | */ 27 | } 28 | 29 | return self; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /PNChart/PNScatterChartDataItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNScatterChartDataItem.m 3 | // PNChartDemo 4 | // 5 | // Created by Alireza Arabi on 12/4/14. 6 | // Copyright (c) 2014 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNScatterChartDataItem.h" 10 | 11 | @interface PNScatterChartDataItem () 12 | 13 | - (id)initWithX:(CGFloat)x AndWithY:(CGFloat)y; 14 | 15 | @property (readwrite) CGFloat x; // should be within the x range 16 | @property (readwrite) CGFloat y; // should be within the y range 17 | 18 | @end 19 | 20 | @implementation PNScatterChartDataItem 21 | 22 | + (PNScatterChartDataItem *)dataItemWithX:(CGFloat)x AndWithY:(CGFloat)y 23 | { 24 | return [[PNScatterChartDataItem alloc] initWithX:x AndWithY:y]; 25 | } 26 | 27 | - (id)initWithX:(CGFloat)x AndWithY:(CGFloat)y 28 | { 29 | if ((self = [super init])) { 30 | self.x = x; 31 | self.y = y; 32 | } 33 | 34 | return self; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /PNChart/PNChart.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNChart.h 3 | // PNChart 4 | // 5 | // Created by Everton Cunha on 06/03/18. 6 | // Copyright © 2018 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for PNChart. 12 | FOUNDATION_EXPORT double PNChartVersionNumber; 13 | 14 | //! Project version string for PNChart. 15 | FOUNDATION_EXPORT const unsigned char PNChartVersionString[]; 16 | 17 | #import "PNScatterChart.h" 18 | #import "PNScatterChartData.h" 19 | #import "PNScatterChartDataItem.h" 20 | #import "PNChart.h" 21 | #import "PNChartDelegate.h" 22 | #import "PNChartLabel.h" 23 | #import "PNColor.h" 24 | #import "PNBar.h" 25 | #import "PNBarChart.h" 26 | #import "PNCircleChart.h" 27 | #import "PNLineChart.h" 28 | #import "PNLineChartData.h" 29 | #import "PNLineChartDataItem.h" 30 | #import "PNPieChart.h" 31 | #import "PNPieChartDataItem.h" 32 | #import "PNGenericChart.h" 33 | #import "PNRadarChart.h" 34 | #import "PNRadarChartDataItem.h" 35 | #import "PNChart.h" 36 | -------------------------------------------------------------------------------- /PNChart/PNChartDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNChartDelegate.h 3 | // PNChartDemo 4 | // 5 | // Created by kevinzhow on 13-12-11. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol PNChartDelegate 12 | @optional 13 | /** 14 | * Callback method that gets invoked when the user taps on the chart line. 15 | */ 16 | - (void)userClickedOnLinePoint:(CGPoint)point lineIndex:(NSInteger)lineIndex; 17 | 18 | /** 19 | * Callback method that gets invoked when the user taps on a chart line key point. 20 | */ 21 | - (void)userClickedOnLineKeyPoint:(CGPoint)point 22 | lineIndex:(NSInteger)lineIndex 23 | pointIndex:(NSInteger)pointIndex; 24 | 25 | /** 26 | * Callback method that gets invoked when the user taps on a chart bar. 27 | */ 28 | - (void)userClickedOnBarAtIndex:(NSInteger)barIndex; 29 | 30 | 31 | - (void)userClickedOnPieIndexItem:(NSInteger)pieIndex; 32 | - (void)didUnselectPieItem; 33 | @end 34 | -------------------------------------------------------------------------------- /PNChart/PNLineChartDataItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jörg Polakowski on 14/12/13. 3 | // Copyright (c) 2013 kevinzhow. All rights reserved. 4 | // 5 | 6 | #import "PNLineChartDataItem.h" 7 | 8 | @interface PNLineChartDataItem () 9 | 10 | - (id)initWithY:(CGFloat)y andRawY:(CGFloat)rawY; 11 | 12 | @property (readwrite) CGFloat y; // should be within the y range 13 | @property (readwrite) CGFloat rawY; // this is the raw value, used for point label. 14 | 15 | @end 16 | 17 | @implementation PNLineChartDataItem 18 | 19 | + (PNLineChartDataItem *)dataItemWithY:(CGFloat)y 20 | { 21 | return [[PNLineChartDataItem alloc] initWithY:y andRawY:y]; 22 | } 23 | 24 | + (PNLineChartDataItem *)dataItemWithY:(CGFloat)y andRawY:(CGFloat)rawY { 25 | return [[PNLineChartDataItem alloc] initWithY:y andRawY:rawY]; 26 | } 27 | 28 | - (id)initWithY:(CGFloat)y andRawY:(CGFloat)rawY 29 | { 30 | if ((self = [super init])) { 31 | self.y = y; 32 | self.rawY = rawY; 33 | } 34 | 35 | return self; 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /PNChart/PNPieChartDataItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNPieChartDataItem.m 3 | // PNChartDemo 4 | // 5 | // Created by Hang Zhang on 14-5-5. 6 | // Copyright (c) 2014年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNPieChartDataItem.h" 10 | #import 11 | 12 | @implementation PNPieChartDataItem 13 | 14 | 15 | + (instancetype)dataItemWithValue:(CGFloat)value 16 | color:(UIColor*)color{ 17 | PNPieChartDataItem *item = [PNPieChartDataItem new]; 18 | item.value = value; 19 | item.color = color; 20 | return item; 21 | } 22 | 23 | + (instancetype)dataItemWithValue:(CGFloat)value 24 | color:(UIColor*)color 25 | description:(NSString *)description { 26 | PNPieChartDataItem *item = [PNPieChartDataItem dataItemWithValue:value color:color]; 27 | item.textDescription = description; 28 | return item; 29 | } 30 | 31 | - (void)setValue:(CGFloat)value{ 32 | NSAssert(value >= 0, @"value should >= 0"); 33 | if (value != _value){ 34 | _value = value; 35 | } 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /PNChartDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Kevin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /PNChart/PNBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNBar.h 3 | // PNChartDemo 4 | // 5 | // Created by kevin on 11/7/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface PNBar : UIView 13 | 14 | 15 | - (void)rollBack; 16 | 17 | @property (nonatomic) float grade; 18 | @property (nonatomic) float maxDivisor; 19 | 20 | @property (nonatomic) CAShapeLayer *chartLine; 21 | @property (nonatomic) UIColor *barColor; 22 | @property (nonatomic) UIColor *barColorGradientStart; 23 | @property (nonatomic) CGFloat barRadius; 24 | @property (nonatomic) CAShapeLayer *gradientMask; 25 | 26 | @property (nonatomic) CAShapeLayer *gradeLayer; 27 | @property (nonatomic) CATextLayer* textLayer; 28 | 29 | /** Text color for all bars in the chart. */ 30 | @property (nonatomic) UIColor * labelTextColor; 31 | 32 | @property (nonatomic, assign) BOOL isNegative; //!< 是否是负数 33 | @property (nonatomic, assign) BOOL isShowNumber; //!< 是否显示numbers 34 | 35 | /** Display the bar with or without animation. Default is YES. **/ 36 | @property (nonatomic) BOOL displayAnimated; 37 | @end 38 | -------------------------------------------------------------------------------- /PNChart/PNScatterChartData.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNScatterChartData.h 3 | // PNChartDemo 4 | // 5 | // Created by Alireza Arabi on 12/4/14. 6 | // Copyright (c) 2014 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | typedef NS_ENUM(NSUInteger, PNScatterChartPointStyle) { 13 | PNScatterChartPointStyleCircle = 0, 14 | PNScatterChartPointStyleSquare = 1, 15 | }; 16 | 17 | @class PNScatterChartDataItem; 18 | 19 | typedef PNScatterChartDataItem *(^LCScatterChartDataGetter)(NSUInteger item); 20 | 21 | @interface PNScatterChartData : NSObject 22 | 23 | @property (strong) UIColor *fillColor; 24 | @property (strong) UIColor *strokeColor; 25 | 26 | @property NSUInteger itemCount; 27 | @property (copy) LCScatterChartDataGetter getData; 28 | 29 | @property (nonatomic, assign) PNScatterChartPointStyle inflexionPointStyle; 30 | 31 | /** 32 | * If PNLineChartPointStyle is circle, this returns the circle's diameter. 33 | * If PNLineChartPointStyle is square, each point is a square with each side equal in length to this value. 34 | */ 35 | @property (nonatomic, assign) CGFloat size; 36 | 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /PNChart/PNGenericChart.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNGenericChart.m 3 | // PNChartDemo 4 | // 5 | // Created by Andi Palo on 26/02/15. 6 | // Copyright (c) 2015 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNGenericChart.h" 10 | 11 | @interface PNGenericChart () 12 | 13 | 14 | 15 | @end 16 | 17 | @implementation PNGenericChart 18 | 19 | /* 20 | // Only override drawRect: if you perform custom drawing. 21 | // An empty implementation adversely affects performance during animation. 22 | - (void)drawRect:(CGRect)rect { 23 | // Drawing code 24 | } 25 | */ 26 | 27 | - (void) setupDefaultValues{ 28 | self.hasLegend = YES; 29 | self.legendPosition = PNLegendPositionBottom; 30 | self.legendStyle = PNLegendItemStyleStacked; 31 | self.labelRowsInSerialMode = 1; 32 | self.displayAnimated = YES; 33 | } 34 | 35 | 36 | 37 | /** 38 | * to be implemented in subclass 39 | */ 40 | - (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth{ 41 | [self doesNotRecognizeSelector:_cmd]; 42 | return nil; 43 | } 44 | 45 | - (void) setLabelRowsInSerialMode:(NSUInteger)num{ 46 | if (self.legendStyle == PNLegendItemStyleSerial) { 47 | _labelRowsInSerialMode = num; 48 | }else{ 49 | _labelRowsInSerialMode = 1; 50 | } 51 | } 52 | 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /PNChartDemo/PNChartDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | Launch Screen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /PNChartDemo/PCChartViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PCChartViewController.h 3 | // PNChartDemo 4 | // 5 | // Created by kevin on 11/7/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PNChartDelegate.h" 11 | #import "PNChart/PNChart.h" 12 | 13 | @interface PCChartViewController : UIViewController 14 | 15 | @property (nonatomic) PNLineChart * lineChart; 16 | @property (nonatomic) PNBarChart * barChart; 17 | @property (nonatomic) PNCircleChart * circleChart; 18 | @property (nonatomic) PNPieChart *pieChart; 19 | @property (nonatomic) PNScatterChart *scatterChart; 20 | @property (nonatomic) PNRadarChart *radarChart; 21 | @property (weak, nonatomic) IBOutlet UILabel *centerSwitchLabel; 22 | 23 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 24 | 25 | - (IBAction)changeValue:(id)sender; 26 | @property (weak, nonatomic) IBOutlet UIButton *changeValueButton; 27 | 28 | @property (weak, nonatomic) IBOutlet UISwitch *animationsSwitch; 29 | @property (weak, nonatomic) IBOutlet UILabel *animationsLabel; 30 | @property (weak, nonatomic) IBOutlet UISwitch *leftSwitch; 31 | @property (weak, nonatomic) IBOutlet UISwitch *centerSwitch; 32 | @property (weak, nonatomic) IBOutlet UISwitch *rightSwitch; 33 | @property (weak, nonatomic) IBOutlet UILabel *leftLabel; 34 | @property (weak, nonatomic) IBOutlet UILabel *rightLabel; 35 | 36 | - (IBAction)rightSwitchChanged:(id)sender; 37 | - (IBAction)leftSwitchChanged:(id)sender; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /UICountingLabel.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | typedef NS_ENUM(NSInteger, UILabelCountingMethod) { 5 | UILabelCountingMethodEaseInOut, 6 | UILabelCountingMethodEaseIn, 7 | UILabelCountingMethodEaseOut, 8 | UILabelCountingMethodLinear, 9 | UILabelCountingMethodEaseInBounce, 10 | UILabelCountingMethodEaseOutBounce 11 | }; 12 | 13 | typedef NSString* (^UICountingLabelFormatBlock)(CGFloat value); 14 | typedef NSAttributedString* (^UICountingLabelAttributedFormatBlock)(CGFloat value); 15 | 16 | @interface UICountingLabel : UILabel 17 | 18 | @property (nonatomic, strong) NSString *format; 19 | @property (nonatomic, assign) UILabelCountingMethod method; 20 | @property (nonatomic, assign) NSTimeInterval animationDuration; 21 | 22 | @property (nonatomic, copy) UICountingLabelFormatBlock formatBlock; 23 | @property (nonatomic, copy) UICountingLabelAttributedFormatBlock attributedFormatBlock; 24 | @property (nonatomic, copy) void (^completionBlock)(void); 25 | 26 | -(void)countFrom:(CGFloat)startValue to:(CGFloat)endValue; 27 | -(void)countFrom:(CGFloat)startValue to:(CGFloat)endValue withDuration:(NSTimeInterval)duration; 28 | 29 | -(void)countFromCurrentValueTo:(CGFloat)endValue; 30 | -(void)countFromCurrentValueTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration; 31 | 32 | -(void)countFromZeroTo:(CGFloat)endValue; 33 | -(void)countFromZeroTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration; 34 | 35 | - (CGFloat)currentValue; 36 | 37 | @end 38 | 39 | -------------------------------------------------------------------------------- /PNChart/PNGenericChart.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNGenericChart.h 3 | // PNChartDemo 4 | // 5 | // Created by Andi Palo on 26/02/15. 6 | // Copyright (c) 2015 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSUInteger, PNLegendPosition) { 12 | PNLegendPositionTop = 0, 13 | PNLegendPositionBottom = 1, 14 | PNLegendPositionLeft = 2, 15 | PNLegendPositionRight = 3 16 | }; 17 | 18 | typedef NS_ENUM(NSUInteger, PNLegendItemStyle) { 19 | PNLegendItemStyleStacked = 0, 20 | PNLegendItemStyleSerial = 1 21 | }; 22 | 23 | @interface PNGenericChart : UIView 24 | 25 | @property (assign, nonatomic) BOOL hasLegend; 26 | @property (assign, nonatomic) PNLegendPosition legendPosition; 27 | @property (assign, nonatomic) PNLegendItemStyle legendStyle; 28 | 29 | @property (assign, nonatomic) UIFont *legendFont; 30 | @property (assign, nonatomic) UIColor *legendFontColor; 31 | @property (assign, nonatomic) NSUInteger labelRowsInSerialMode; 32 | 33 | /** Display the chart with or without animation. Default is YES. **/ 34 | @property (nonatomic) BOOL displayAnimated; 35 | 36 | /** 37 | * returns the Legend View, or nil if no chart data is present. 38 | * The origin of the legend frame is 0,0 but you can set it with setFrame:(CGRect) 39 | * 40 | * @param mWidth Maximum width of legend. Height will depend on this and font size 41 | * 42 | * @return UIView of Legend 43 | */ 44 | - (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth; 45 | 46 | 47 | - (void) setupDefaultValues; 48 | @end 49 | -------------------------------------------------------------------------------- /PNChart/PNLineChartData.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jörg Polakowski on 14/12/13. 3 | // Copyright (c) 2013 kevinzhow. All rights reserved. 4 | // 5 | 6 | #import "PNLineChartData.h" 7 | 8 | 9 | @implementation PNLineChartColorRange 10 | 11 | - (id)initWithRange:(NSRange)range color:(UIColor *)color { 12 | self = [super init]; 13 | if (self) { 14 | self.range = range; 15 | self.color = color; 16 | self.inclusive = NO; 17 | } 18 | return self; 19 | } 20 | 21 | 22 | - (id)initWithRange:(NSRange)range color:(UIColor *)color inclusive:(BOOL)isInclusive { 23 | self = [super init]; 24 | if (self) { 25 | self.range = range; 26 | self.color = color; 27 | self.inclusive = isInclusive; 28 | } 29 | return self; 30 | } 31 | 32 | 33 | - (id)copyWithZone:(NSZone *)zone { 34 | PNLineChartColorRange *copy = [[self class] allocWithZone:zone]; 35 | copy.color = self.color; 36 | copy.range = self.range; 37 | return copy; 38 | } 39 | 40 | @end 41 | 42 | @implementation PNLineChartData 43 | 44 | - (id)init 45 | { 46 | self = [super init]; 47 | if (self) { 48 | [self setupDefaultValues]; 49 | } 50 | 51 | return self; 52 | } 53 | 54 | - (void)setupDefaultValues 55 | { 56 | _inflexionPointStyle = PNLineChartPointStyleNone; 57 | _inflexionPointWidth = 6.f; 58 | _lineWidth = 2.f; 59 | _alpha = 1.f; 60 | _showPointLabel = NO; 61 | _pointLabelColor = [UIColor blackColor]; 62 | _pointLabelFormat = @"%1.f"; 63 | _rangeColors = nil; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | -------------------------------------------------------------------------------- /PNChartDemo/PCChartsTableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PCChartsTableViewController.m 3 | // PNChartDemo 4 | // 5 | // Created by kevinzhow on 13-12-1. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PCChartsTableViewController.h" 10 | 11 | @implementation PCChartsTableViewController 12 | 13 | // In a story board-based application, you will often want to do a little preparation before navigation 14 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 15 | { 16 | // Get the new view controller using [segue destinationViewController]. 17 | // Pass the selected object to the new view controller. 18 | 19 | UIViewController * viewController = [segue destinationViewController]; 20 | 21 | if ([segue.identifier isEqualToString:@"lineChart"]) { 22 | 23 | //Add line chart 24 | 25 | viewController.title = @"Line Chart"; 26 | 27 | } else if ([segue.identifier isEqualToString:@"barChart"]) 28 | { 29 | //Add bar chart 30 | 31 | viewController.title = @"Bar Chart"; 32 | } else if ([segue.identifier isEqualToString:@"circleChart"]) 33 | { 34 | //Add circle chart 35 | 36 | viewController.title = @"Circle Chart"; 37 | 38 | } else if ([segue.identifier isEqualToString:@"pieChart"]) 39 | { 40 | //Add pie chart 41 | 42 | viewController.title = @"Pie Chart"; 43 | } else if ([segue.identifier isEqualToString:@"scatterChart"]) 44 | { 45 | //Add scatter chart 46 | 47 | viewController.title = @"Scatter Chart"; 48 | }else if ([segue.identifier isEqualToString:@"radarChart"]) 49 | { 50 | //Add radar chart 51 | 52 | viewController.title = @"Radar Chart"; 53 | } 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /PNChart/PNRadarChart.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNRadarChart.h 3 | // PNChartDemo 4 | // 5 | // Created by Lei on 15/7/1. 6 | // Copyright (c) 2015年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PNGenericChart.h" 11 | #import "PNRadarChartDataItem.h" 12 | 13 | #define MAXCIRCLE 20 14 | 15 | typedef NS_ENUM(NSUInteger, PNRadarChartLabelStyle) { 16 | PNRadarChartLabelStyleCircle = 0, 17 | PNRadarChartLabelStyleHorizontal, 18 | PNRadarChartLabelStyleHidden, 19 | }; 20 | 21 | @interface PNRadarChart : PNGenericChart 22 | 23 | -(id)initWithFrame:(CGRect)frame items:(NSArray *)items valueDivider:(CGFloat)unitValue; 24 | 25 | /** 26 | *Draws the chart in an animated fashion. 27 | */ 28 | -(void)strokeChart; 29 | 30 | /** Array of `RadarChartDataItem` objects, one for each corner. */ 31 | @property (nonatomic) NSArray *chartData; 32 | /** The unit of this chart ,default is 1 */ 33 | @property (nonatomic) CGFloat valueDivider; 34 | /** The maximum for the range of values to display on the chart */ 35 | @property (nonatomic) CGFloat maxValue; 36 | /** Default is gray. */ 37 | @property (nonatomic) UIColor *webColor; 38 | /** Default is green , with an alpha of 0.7 */ 39 | @property (nonatomic) UIColor *plotColor; 40 | /** Default is black */ 41 | @property (nonatomic) UIColor *fontColor; 42 | /** Default is orange */ 43 | @property (nonatomic) UIColor *graduationColor; 44 | /** Default is 15 */ 45 | @property (nonatomic) CGFloat fontSize; 46 | /** Controls the labels display style that around chart */ 47 | @property (nonatomic, assign) PNRadarChartLabelStyle labelStyle; 48 | /** Tap the label will display detail value ,default is YES. */ 49 | @property (nonatomic, assign) BOOL isLabelTouchable; 50 | /** is show graduation on the chart ,default is NO. */ 51 | @property (nonatomic, assign) BOOL isShowGraduation; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /PNChartDemo/PCAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // PCAppDelegate.m 3 | // PNChartDemo 4 | // 5 | // Created by kevin on 11/7/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PCAppDelegate.h" 10 | 11 | @implementation PCAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /PNChart/PNLineChartData.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Jörg Polakowski on 14/12/13. 3 | // Copyright (c) 2013 kevinzhow. All rights reserved. 4 | // 5 | 6 | #import 7 | #import 8 | 9 | typedef NS_ENUM(NSUInteger, PNLineChartPointStyle) { 10 | PNLineChartPointStyleNone = 0, 11 | PNLineChartPointStyleCircle = 1, 12 | PNLineChartPointStyleSquare = 3, 13 | PNLineChartPointStyleTriangle = 4 14 | }; 15 | 16 | @class PNLineChartDataItem; 17 | 18 | typedef PNLineChartDataItem *(^LCLineChartDataGetter)(NSUInteger item); 19 | 20 | @interface PNLineChartColorRange : NSObject 21 | 22 | @property(nonatomic) NSRange range; 23 | @property(nonatomic) BOOL inclusive; 24 | @property(nonatomic, retain) UIColor *color; 25 | 26 | - (id)initWithRange:(NSRange)range color:(UIColor *)color; 27 | - (id)initWithRange:(NSRange)range color:(UIColor *)color inclusive:(BOOL)isInclusive; 28 | 29 | @end 30 | 31 | @interface PNLineChartData : NSObject 32 | 33 | @property (strong) UIColor *color; 34 | @property (nonatomic) CGFloat alpha; 35 | @property NSUInteger itemCount; 36 | @property (copy) LCLineChartDataGetter getData; 37 | @property (strong, nonatomic) NSString *dataTitle; 38 | 39 | @property (nonatomic) BOOL showPointLabel; 40 | @property (nonatomic) UIColor *pointLabelColor; 41 | @property (nonatomic) UIFont *pointLabelFont; 42 | @property (nonatomic) NSString *pointLabelFormat; 43 | 44 | @property (nonatomic, assign) PNLineChartPointStyle inflexionPointStyle; 45 | @property (nonatomic) UIColor *inflexionPointColor; 46 | 47 | /** 48 | * if rangeColor is set and the lineChartData values are within any 49 | * of the given range then use the rangeColor.color otherwise use 50 | * self.color for the rest of the graph 51 | */ 52 | @property(strong) NSArray *rangeColors; 53 | 54 | /** 55 | * If PNLineChartPointStyle is circle, this returns the circle's diameter. 56 | * If PNLineChartPointStyle is square, each point is a square with each side equal in length to this value. 57 | */ 58 | @property (nonatomic, assign) CGFloat inflexionPointWidth; 59 | 60 | @property (nonatomic, assign) CGFloat lineWidth; 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /PNChart/PNPieChart.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNPieChart.h 3 | // PNChartDemo 4 | // 5 | // Created by Hang Zhang on 14-5-5. 6 | // Copyright (c) 2014年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PNPieChartDataItem.h" 11 | #import "PNGenericChart.h" 12 | #import "PNChartDelegate.h" 13 | 14 | #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_10_0 15 | @interface PNPieChart : PNGenericChart 16 | #else 17 | @interface PNPieChart : PNGenericChart 18 | #endif 19 | 20 | - (id)initWithFrame:(CGRect)frame items:(NSArray *)items; 21 | 22 | @property (nonatomic, readonly) NSArray *items; 23 | 24 | /** Default is 18-point Avenir Medium. */ 25 | @property (nonatomic) UIFont *descriptionTextFont; 26 | 27 | /** Default is white. */ 28 | @property (nonatomic) UIColor *descriptionTextColor; 29 | 30 | /** Default is black, with an alpha of 0.4. */ 31 | @property (nonatomic) UIColor *descriptionTextShadowColor; 32 | 33 | /** Default is CGSizeMake(0, 1). */ 34 | @property (nonatomic) CGSize descriptionTextShadowOffset; 35 | 36 | /** Default is 1.0. */ 37 | @property (nonatomic) NSTimeInterval duration; 38 | 39 | /** Show only values, this is useful when legend is present */ 40 | @property (nonatomic) BOOL showOnlyValues; 41 | 42 | /** Show absolute values not relative i.e. percentages */ 43 | @property (nonatomic) BOOL showAbsoluteValues; 44 | 45 | /** Hide percentage labels less than cutoff value */ 46 | @property (nonatomic, assign) CGFloat labelPercentageCutoff; 47 | 48 | /** Default YES. */ 49 | @property (nonatomic) BOOL shouldHighlightSectorOnTouch; 50 | 51 | /** Current outer radius. Override recompute() to change this. **/ 52 | @property (nonatomic) CGFloat outerCircleRadius; 53 | 54 | /** Current inner radius. Override recompute() to change this. **/ 55 | @property (nonatomic) CGFloat innerCircleRadius; 56 | 57 | @property (nonatomic, weak) id delegate; 58 | 59 | /** Update chart items. Does not update chart itself. */ 60 | - (void)updateChartData:(NSArray *)data; 61 | 62 | /** Multiple selection */ 63 | @property (nonatomic, assign) BOOL enableMultipleSelection; 64 | 65 | /** show only tiles, not values or percentage */ 66 | @property (nonatomic) BOOL hideValues; 67 | 68 | - (void)strokeChart; 69 | 70 | - (void)recompute; 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /PNChart/PNScatterChart.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNScatterChart.h 3 | // PNChartDemo 4 | // 5 | // Created by Alireza Arabi on 12/4/14. 6 | // Copyright (c) 2014 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "PNChartDelegate.h" 12 | #import "PNGenericChart.h" 13 | #import "PNScatterChartData.h" 14 | #import "PNScatterChartDataItem.h" 15 | 16 | @interface PNScatterChart : PNGenericChart 17 | 18 | @property (nonatomic, retain) id delegate; 19 | 20 | /** Array of `ScatterChartData` objects, one for each line. */ 21 | @property (nonatomic) NSArray *chartData; 22 | 23 | /** Controls whether to show the coordinate axis. Default is NO. */ 24 | @property (nonatomic, getter = isShowCoordinateAxis) BOOL showCoordinateAxis; 25 | @property (nonatomic) UIColor *axisColor; 26 | @property (nonatomic) CGFloat axisWidth; 27 | 28 | /** String formatter for float values in x-axis/y-axis labels. If not set, defaults to @"%1.f" */ 29 | @property (nonatomic, strong) NSString *xLabelFormat; 30 | @property (nonatomic, strong) NSString *yLabelFormat; 31 | 32 | /** Default is true. */ 33 | @property (nonatomic) BOOL showLabel; 34 | 35 | /** Default is 18-point Avenir Medium. */ 36 | @property (nonatomic) UIFont *descriptionTextFont; 37 | 38 | /** Default is white. */ 39 | @property (nonatomic) UIColor *descriptionTextColor; 40 | 41 | /** Default is black, with an alpha of 0.4. */ 42 | @property (nonatomic) UIColor *descriptionTextShadowColor; 43 | 44 | /** Default is CGSizeMake(0, 1). */ 45 | @property (nonatomic) CGSize descriptionTextShadowOffset; 46 | 47 | /** Default is 1.0. */ 48 | @property (nonatomic) NSTimeInterval duration; 49 | 50 | @property (nonatomic) CGFloat AxisX_minValue; 51 | @property (nonatomic) CGFloat AxisX_maxValue; 52 | 53 | @property (nonatomic) CGFloat AxisY_minValue; 54 | @property (nonatomic) CGFloat AxisY_maxValue; 55 | 56 | - (void) setAxisXWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks; 57 | - (void) setAxisYWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks; 58 | - (void) setAxisXLabel:(NSArray *)array; 59 | - (void) setAxisYLabel:(NSArray *)array; 60 | - (void) setup; 61 | - (void) drawLineFromPoint : (CGPoint) startPoint ToPoint : (CGPoint) endPoint WithLineWith : (CGFloat) lineWidth AndWithColor : (UIColor*) color; 62 | 63 | /** 64 | * Update Chart Value 65 | */ 66 | 67 | - (void)updateChartData:(NSArray *)data; 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /PNChart/PNCircleChart.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNCircleChart.h 3 | // PNChartDemo 4 | // 5 | // Created by kevinzhow on 13-11-30. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PNColor.h" 11 | #import "UICountingLabel.h" 12 | 13 | typedef NS_ENUM (NSUInteger, PNChartFormatType) { 14 | PNChartFormatTypePercent, 15 | PNChartFormatTypeDollar, 16 | PNChartFormatTypeNone, 17 | PNChartFormatTypeDecimal, 18 | PNChartFormatTypeDecimalTwoPlaces, 19 | }; 20 | 21 | #define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI) 22 | 23 | @interface PNCircleChart : UIView 24 | 25 | - (void)strokeChart; 26 | - (void)growChartByAmount:(NSNumber *)growAmount; 27 | - (void)updateChartByCurrent:(NSNumber *)current; 28 | - (void)updateChartByCurrent:(NSNumber *)current byTotal:(NSNumber *)total; 29 | - (id)initWithFrame:(CGRect)frame 30 | total:(NSNumber *)total 31 | current:(NSNumber *)current 32 | clockwise:(BOOL)clockwise; 33 | 34 | - (id)initWithFrame:(CGRect)frame 35 | total:(NSNumber *)total 36 | current:(NSNumber *)current 37 | clockwise:(BOOL)clockwise 38 | shadow:(BOOL)hasBackgroundShadow 39 | shadowColor:(UIColor *)backgroundShadowColor; 40 | 41 | - (id)initWithFrame:(CGRect)frame 42 | total:(NSNumber *)total 43 | current:(NSNumber *)current 44 | clockwise:(BOOL)clockwise 45 | shadow:(BOOL)hasBackgroundShadow 46 | shadowColor:(UIColor *)backgroundShadowColor 47 | displayCountingLabel:(BOOL)displayCountingLabel; 48 | 49 | - (id)initWithFrame:(CGRect)frame 50 | total:(NSNumber *)total 51 | current:(NSNumber *)current 52 | clockwise:(BOOL)clockwise 53 | shadow:(BOOL)hasBackgroundShadow 54 | shadowColor:(UIColor *)backgroundShadowColor 55 | displayCountingLabel:(BOOL)displayCountingLabel 56 | overrideLineWidth:(NSNumber *)overrideLineWidth; 57 | 58 | @property (strong, nonatomic) UICountingLabel *countingLabel; 59 | @property (nonatomic) UIColor *strokeColor; 60 | @property (nonatomic) UIColor *strokeColorGradientStart; 61 | @property (nonatomic) NSNumber *total; 62 | @property (nonatomic) NSNumber *current; 63 | @property (nonatomic) NSNumber *lineWidth; 64 | @property (nonatomic) NSTimeInterval duration; 65 | @property (nonatomic) PNChartFormatType chartType; 66 | 67 | @property (nonatomic) CAShapeLayer *circle; 68 | @property (nonatomic) CAShapeLayer *gradientMask; 69 | @property (nonatomic) CAShapeLayer *circleBackground; 70 | 71 | @property (nonatomic) BOOL displayCountingLabel; 72 | @property (nonatomic) BOOL displayAnimated; 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /PNChartDemo.xcodeproj/xcshareddata/xcschemes/PNChart.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /PNChart/PNColor.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNColor.h 3 | // PNChart 4 | // 5 | // Created by kevin on 13-6-8. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | /* 13 | * System Versioning Preprocessor Macros 14 | */ 15 | 16 | #define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width) 17 | 18 | #define PNGrey [UIColor colorWithRed:246.0 / 255.0 green:246.0 / 255.0 blue:246.0 / 255.0 alpha:1.0f] 19 | #define PNLightBlue [UIColor colorWithRed:94.0 / 255.0 green:147.0 / 255.0 blue:196.0 / 255.0 alpha:1.0f] 20 | #define PNGreen [UIColor colorWithRed:77.0 / 255.0 green:186.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] 21 | #define PNTitleColor [UIColor colorWithRed:0.0 / 255.0 green:189.0 / 255.0 blue:113.0 / 255.0 alpha:1.0f] 22 | #define PNButtonGrey [UIColor colorWithRed:141.0 / 255.0 green:141.0 / 255.0 blue:141.0 / 255.0 alpha:1.0f] 23 | #define PNLightGreen [UIColor colorWithRed:77.0 / 255.0 green:216.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] 24 | #define PNFreshGreen [UIColor colorWithRed:77.0 / 255.0 green:196.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] 25 | #define PNDeepGreen [UIColor colorWithRed:77.0 / 255.0 green:176.0 / 255.0 blue:122.0 / 255.0 alpha:1.0f] 26 | #define PNRed [UIColor colorWithRed:245.0 / 255.0 green:94.0 / 255.0 blue:78.0 / 255.0 alpha:1.0f] 27 | #define PNMauve [UIColor colorWithRed:88.0 / 255.0 green:75.0 / 255.0 blue:103.0 / 255.0 alpha:1.0f] 28 | #define PNBrown [UIColor colorWithRed:119.0 / 255.0 green:107.0 / 255.0 blue:95.0 / 255.0 alpha:1.0f] 29 | #define PNBlue [UIColor colorWithRed:82.0 / 255.0 green:116.0 / 255.0 blue:188.0 / 255.0 alpha:1.0f] 30 | #define PNDarkBlue [UIColor colorWithRed:121.0 / 255.0 green:134.0 / 255.0 blue:142.0 / 255.0 alpha:1.0f] 31 | #define PNYellow [UIColor colorWithRed:242.0 / 255.0 green:197.0 / 255.0 blue:117.0 / 255.0 alpha:1.0f] 32 | #define PNWhite [UIColor colorWithRed:255.0 / 255.0 green:255.0 / 255.0 blue:255.0 / 255.0 alpha:1.0f] 33 | #define PNDeepGrey [UIColor colorWithRed:99.0 / 255.0 green:99.0 / 255.0 blue:99.0 / 255.0 alpha:1.0f] 34 | #define PNPinkGrey [UIColor colorWithRed:200.0 / 255.0 green:193.0 / 255.0 blue:193.0 / 255.0 alpha:1.0f] 35 | #define PNHealYellow [UIColor colorWithRed:245.0 / 255.0 green:242.0 / 255.0 blue:238.0 / 255.0 alpha:1.0f] 36 | #define PNLightGrey [UIColor colorWithRed:225.0 / 255.0 green:225.0 / 255.0 blue:225.0 / 255.0 alpha:1.0f] 37 | #define PNCleanGrey [UIColor colorWithRed:251.0 / 255.0 green:251.0 / 255.0 blue:251.0 / 255.0 alpha:1.0f] 38 | #define PNLightYellow [UIColor colorWithRed:241.0 / 255.0 green:240.0 / 255.0 blue:240.0 / 255.0 alpha:1.0f] 39 | #define PNDarkYellow [UIColor colorWithRed:152.0 / 255.0 green:150.0 / 255.0 blue:159.0 / 255.0 alpha:1.0f] 40 | #define PNPinkDark [UIColor colorWithRed:170.0 / 255.0 green:165.0 / 255.0 blue:165.0 / 255.0 alpha:1.0f] 41 | #define PNCloudWhite [UIColor colorWithRed:244.0 / 255.0 green:244.0 / 255.0 blue:244.0 / 255.0 alpha:1.0f] 42 | #define PNBlack [UIColor colorWithRed:45.0 / 255.0 green:45.0 / 255.0 blue:45.0 / 255.0 alpha:1.0f] 43 | #define PNStarYellow [UIColor colorWithRed:252.0 / 255.0 green:223.0 / 255.0 blue:101.0 / 255.0 alpha:1.0f] 44 | #define PNTwitterColor [UIColor colorWithRed:0.0 / 255.0 green:171.0 / 255.0 blue:243.0 / 255.0 alpha:1.0] 45 | #define PNWeiboColor [UIColor colorWithRed:250.0 / 255.0 green:0.0 / 255.0 blue:33.0 / 255.0 alpha:1.0] 46 | #define PNiOSGreenColor [UIColor colorWithRed:98.0 / 255.0 green:247.0 / 255.0 blue:77.0 / 255.0 alpha:1.0] 47 | 48 | 49 | @interface PNColor : NSObject 50 | 51 | - (UIImage *)imageFromColor:(UIColor *)color; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /PNChart/PNLineChart.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNLineChart.h 3 | // PNChartDemo 4 | // 5 | // Created by kevin on 11/7/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "PNChartDelegate.h" 12 | #import "PNGenericChart.h" 13 | 14 | @interface PNLineChart : PNGenericChart 15 | 16 | /** 17 | * Draws the chart in an animated fashion. 18 | */ 19 | - (void)strokeChart; 20 | 21 | @property (nonatomic, weak) id delegate; 22 | 23 | @property (nonatomic) NSArray *xLabels; 24 | @property (nonatomic) NSArray *yLabels; 25 | 26 | /** 27 | * Array of `LineChartData` objects, one for each line. 28 | */ 29 | @property (nonatomic) NSArray *chartData; 30 | 31 | @property (nonatomic) NSMutableArray *pathPoints; 32 | @property (nonatomic) NSMutableArray *xChartLabels; 33 | @property (nonatomic) NSMutableArray *yChartLabels; 34 | 35 | @property (nonatomic) CGFloat xLabelWidth; 36 | @property (nonatomic) UIFont *xLabelFont; 37 | @property (nonatomic) UIColor *xLabelColor; 38 | @property (nonatomic) CGFloat yValueMax; 39 | @property (nonatomic) CGFloat yFixedValueMax; 40 | @property (nonatomic) CGFloat yFixedValueMin; 41 | @property (nonatomic) CGFloat yValueMin; 42 | @property (nonatomic) NSInteger yLabelNum; 43 | @property (nonatomic) CGFloat yLabelHeight; 44 | @property (nonatomic) UIFont *yLabelFont; 45 | @property (nonatomic) UIColor *yLabelColor; 46 | @property (nonatomic) CGFloat chartCavanHeight; 47 | @property (nonatomic) CGFloat chartCavanWidth; 48 | @property (nonatomic) BOOL showLabel; 49 | @property (nonatomic) BOOL showGenYLabels; 50 | @property (nonatomic) BOOL showYGridLines; 51 | @property (nonatomic) UIColor *yGridLinesColor; 52 | @property (nonatomic) BOOL thousandsSeparator; 53 | 54 | @property (nonatomic) CGFloat chartMarginLeft; 55 | @property (nonatomic) CGFloat chartMarginRight; 56 | @property (nonatomic) CGFloat chartMarginTop; 57 | @property (nonatomic) CGFloat chartMarginBottom; 58 | 59 | /** 60 | * Controls whether to show the coordinate axis. Default is NO. 61 | */ 62 | @property (nonatomic, getter = isShowCoordinateAxis) BOOL showCoordinateAxis; 63 | @property (nonatomic) UIColor *axisColor; 64 | @property (nonatomic) CGFloat axisWidth; 65 | 66 | @property (nonatomic, strong) NSString *xUnit; 67 | @property (nonatomic, strong) NSString *yUnit; 68 | 69 | /** 70 | * String formatter for float values in y-axis labels. If not set, defaults to @"%1.f" 71 | */ 72 | @property (nonatomic, strong) NSString *yLabelFormat; 73 | 74 | /** 75 | * Block formatter for custom string in y-axis labels. If not set, defaults to yLabelFormat 76 | */ 77 | @property (nonatomic, copy) NSString* (^yLabelBlockFormatter)(CGFloat); 78 | 79 | 80 | /** 81 | * Controls whether to curve the line chart or not 82 | */ 83 | @property (nonatomic) BOOL showSmoothLines; 84 | 85 | - (void)setXLabels:(NSArray *)xLabels withWidth:(CGFloat)width; 86 | 87 | /** 88 | * Update Chart Value 89 | */ 90 | 91 | - (void)updateChartData:(NSArray *)data; 92 | 93 | 94 | /** 95 | * returns the Legend View, or nil if no chart data is present. 96 | * The origin of the legend frame is 0,0 but you can set it with setFrame:(CGRect) 97 | * 98 | * @param mWidth Maximum width of legend. Height will depend on this and font size 99 | * 100 | * @return UIView of Legend 101 | */ 102 | - (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth; 103 | 104 | 105 | + (CGSize)sizeOfString:(NSString *)text withWidth:(float)width font:(UIFont *)font; 106 | 107 | + (CGPoint)midPointBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2; 108 | + (CGPoint)controlPointBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2; 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /PNChart/PNBarChart.h: -------------------------------------------------------------------------------- 1 | // 2 | // PNBarChart.h 3 | // PNChartDemo 4 | // 5 | // Created by kevin on 11/7/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PNGenericChart.h" 11 | #import "PNChartDelegate.h" 12 | #import "PNBar.h" 13 | 14 | #define kXLabelMargin 15 15 | #define kYLabelMargin 15 16 | #define kYLabelHeight 11 17 | #define kXLabelHeight 20 18 | 19 | typedef NSString *(^PNYLabelFormatter)(CGFloat yLabelValue); 20 | 21 | @interface PNBarChart : PNGenericChart 22 | 23 | /** 24 | * Draws the chart in an animated fashion. 25 | */ 26 | - (void)strokeChart; 27 | 28 | @property (nonatomic) NSArray *xLabels; 29 | @property (nonatomic) NSArray *yLabels; 30 | @property (nonatomic) NSArray *yValues; 31 | 32 | @property (nonatomic) NSMutableArray * bars; 33 | 34 | @property (nonatomic) CGFloat xLabelWidth; 35 | @property (nonatomic) float yValueMax; 36 | @property (nonatomic) UIColor *strokeColor; 37 | @property (nonatomic) NSArray *strokeColors; 38 | 39 | 40 | /** Update Values. */ 41 | - (void)updateChartData:(NSArray *)data; 42 | 43 | /** Changes chart margin. */ 44 | @property (nonatomic) CGFloat yChartLabelWidth; 45 | 46 | /** Formats the ylabel text. */ 47 | @property (copy) PNYLabelFormatter yLabelFormatter; 48 | 49 | /** Prefix to y label values, none if unset. */ 50 | @property (nonatomic) NSString *yLabelPrefix; 51 | 52 | /** Suffix to y label values, none if unset. */ 53 | @property (nonatomic) NSString *yLabelSuffix; 54 | 55 | @property (nonatomic) CGFloat chartMarginLeft; 56 | @property (nonatomic) CGFloat chartMarginRight; 57 | @property (nonatomic) CGFloat chartMarginTop; 58 | @property (nonatomic) CGFloat chartMarginBottom; 59 | 60 | /** Controls whether labels should be displayed. */ 61 | @property (nonatomic) BOOL showLabel; 62 | 63 | /** Controls whether the chart border line should be displayed. */ 64 | @property (nonatomic) BOOL showChartBorder; 65 | 66 | @property (nonatomic) UIColor *chartBorderColor; 67 | 68 | /** Controls whether the chart Horizontal separator should be displayed. */ 69 | @property (nonatomic, assign) BOOL showLevelLine; 70 | 71 | /** Chart bottom border, co-linear with the x-axis. */ 72 | @property (nonatomic) CAShapeLayer * chartBottomLine; 73 | 74 | /** Chart bottom border, level separator-linear with the x-axis. */ 75 | @property (nonatomic) CAShapeLayer * chartLevelLine; 76 | 77 | /** Chart left border, co-linear with the y-axis. */ 78 | @property (nonatomic) CAShapeLayer * chartLeftLine; 79 | 80 | /** Corner radius for all bars in the chart. */ 81 | @property (nonatomic) CGFloat barRadius; 82 | 83 | /** Width of all bars in the chart. */ 84 | @property (nonatomic) CGFloat barWidth; 85 | 86 | @property (nonatomic) CGFloat labelMarginTop; 87 | 88 | /** Background color of all bars in the chart. */ 89 | @property (nonatomic) UIColor * barBackgroundColor; 90 | 91 | /** Text color for all bars in the chart. */ 92 | @property (nonatomic) UIColor * labelTextColor; 93 | 94 | /** Font for all bars in the chart. */ 95 | @property (nonatomic) UIFont * labelFont; 96 | 97 | /** How many labels on the x-axis to skip in between displaying labels. */ 98 | @property (nonatomic) NSInteger xLabelSkip; 99 | 100 | /** How many labels on the y-axis to skip in between displaying labels. */ 101 | @property (nonatomic) NSInteger yLabelSum; 102 | 103 | /** The maximum for the range of values to display on the y-axis. */ 104 | @property (nonatomic) CGFloat yMaxValue; 105 | 106 | /** The minimum for the range of values to display on the y-axis. */ 107 | @property (nonatomic) CGFloat yMinValue; 108 | 109 | /** Controls whether each bar should have a gradient fill. */ 110 | @property (nonatomic) UIColor *barColorGradientStart; 111 | 112 | /** Controls whether text for x-axis be straight or rotate 45 degree. */ 113 | @property (nonatomic) BOOL rotateForXAxisText; 114 | 115 | @property (nonatomic, weak) id delegate; 116 | 117 | /**whether show gradient bar*/ 118 | @property (nonatomic, assign) BOOL isGradientShow; 119 | 120 | /** whether show numbers*/ 121 | @property (nonatomic, assign) BOOL isShowNumbers; 122 | 123 | @end 124 | -------------------------------------------------------------------------------- /PNChartTests/PNBarChartTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNBarChartTests.m 3 | // PNChartDemo 4 | // 5 | // Created by Viktoras Laukevičius on 18/04/15. 6 | // Copyright (c) 2015 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #define EXP_SHORTHAND 12 | #import 13 | #import "PNBarChart.h" 14 | #import "PNBar.h" 15 | 16 | @interface PNBarChartTests : XCTestCase 17 | 18 | @property (nonatomic, strong) PNBarChart *barChart; 19 | 20 | @end 21 | 22 | @implementation PNBarChartTests 23 | 24 | - (void)setUp 25 | { 26 | [super setUp]; 27 | CGRect frame = CGRectMake(10, 20, 320, 200); 28 | self.barChart = [[PNBarChart alloc] initWithFrame:frame]; 29 | } 30 | 31 | - (void)tearDown 32 | { 33 | self.barChart = nil; 34 | [super tearDown]; 35 | } 36 | 37 | - (void)testXAxisLabels 38 | { 39 | self.barChart.xLabels = @[@"TOne", @"TTwo", @"TThree", @"TFour"]; 40 | expect(self.barChart.subviews.count).equal(4); 41 | for (NSUInteger idx = 0; idx < 4; idx++) { 42 | UILabel *xAxisLabel = self.barChart.subviews[idx]; 43 | expect(xAxisLabel.text).to.equal(self.barChart.xLabels[idx]); 44 | } 45 | } 46 | 47 | - (void)testYAxisLabels 48 | { 49 | self.barChart.yLabelFormatter = ^(CGFloat value) { 50 | return [NSString stringWithFormat:@"Value %zi", (NSUInteger)value]; 51 | }; 52 | self.barChart.yValues = @[@1, @10, @5, @4, @7]; 53 | NSArray *expectedResults = @[@10, @8, @6, @5, @3, @1]; 54 | for (NSUInteger idx = 0; idx < 4; idx++) { 55 | UILabel *yAxisLabel = self.barChart.subviews[idx]; 56 | expect(yAxisLabel.text).to.equal([NSString stringWithFormat:@"Value %@", expectedResults[idx]]); 57 | } 58 | } 59 | 60 | - (void)testLabelsVisibility 61 | { 62 | self.barChart.showLabel = NO; 63 | self.barChart.yLabelFormatter = ^(CGFloat value) { 64 | return [NSString stringWithFormat:@"Value %zi", (NSUInteger)value]; 65 | }; 66 | self.barChart.xLabels = @[@"TOne", @"TTwo", @"TThree", @"TFour"]; 67 | self.barChart.yValues = @[@1, @10, @5, @4, @7]; 68 | expect(self.barChart.subviews.count).to.equal(0); 69 | } 70 | 71 | - (void)testChartBars 72 | { 73 | self.barChart.barBackgroundColor = [UIColor greenColor]; 74 | self.barChart.yLabelFormatter = ^(CGFloat value) { 75 | return [NSString stringWithFormat:@"Value %zi", (NSUInteger)value]; 76 | }; 77 | self.barChart.yValues = @[@1, @2, @3]; 78 | NSArray *strokeColour = @[[UIColor greenColor], [UIColor redColor], [UIColor purpleColor]]; 79 | self.barChart.strokeColors = strokeColour; 80 | [self.barChart strokeChart]; 81 | for (NSUInteger idx = 0; idx < self.barChart.bars.count; idx++) { 82 | PNBar *bar = self.barChart.bars[idx]; 83 | expect(bar.backgroundColor).to.equal([UIColor greenColor]); 84 | expect(bar.barColor).to.equal(strokeColour[idx]); 85 | } 86 | } 87 | 88 | - (void)testStrokeColor 89 | { 90 | self.barChart.yLabelFormatter = ^(CGFloat value) { 91 | return [NSString stringWithFormat:@"Value %zi", (NSUInteger)value]; 92 | }; 93 | self.barChart.yValues = @[@1, @2, @3]; 94 | self.barChart.strokeColor = [UIColor magentaColor]; 95 | self.barChart.strokeColors = @[[UIColor greenColor], [UIColor redColor]]; 96 | [self.barChart strokeChart]; 97 | for (NSUInteger idx = 0; idx < self.barChart.bars.count; idx++) { 98 | PNBar *bar = self.barChart.bars[idx]; 99 | expect(bar.barColor).equal(self.barChart.strokeColor); 100 | } 101 | } 102 | 103 | - (void)testMaxValue 104 | { 105 | self.barChart.yLabelFormatter = ^(CGFloat value) { 106 | return [NSString stringWithFormat:@"Value %zi", (NSUInteger)value]; 107 | }; 108 | self.barChart.yMaxValue = 8; 109 | self.barChart.yValues = @[@10, @8, @7, @3]; 110 | NSArray *expectedResults = @[@8, @6, @4, @2]; 111 | for (NSUInteger idx = 0; idx < expectedResults.count; idx++) { 112 | UILabel *yAxisLabel = self.barChart.subviews[idx]; 113 | expect(yAxisLabel.text).to.equal([NSString stringWithFormat:@"Value %@", expectedResults[idx]]); 114 | } 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /PNChartDemo/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /PNChart.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint PNChart.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "PNChart" 19 | s.version = "0.8.11" 20 | s.summary = "A simple and beautiful chart lib with animation used in Piner for iOS" 21 | 22 | s.description = <<-DESC 23 | #PNChart 24 | 25 | [![Build Status](https://travis-ci.org/kevinzhow/PNChart.png?branch=master)](https://travis-ci.org/kevinzhow/PNChart) 26 | 27 | You can also find swift version at here https://github.com/kevinzhow/PNChart-Swift 28 | 29 | A simple and beautiful chart lib with **animation** used in [Piner](https://itunes.apple.com/us/app/piner/id637706410) and [CoinsMan](https://itunes.apple.com/us/app/coinsman/id772163893) for iOS 30 | 31 | [![](https://dl.dropboxusercontent.com/u/1599662/pnchart.gif)](https://dl.dropboxusercontent.com/u/1599662/pnchart.gif) 32 | 33 | ## Requirements 34 | 35 | PNChart works on iOS 7.0 and later version and is compatible with ARC projects. It depends on the following Apple frameworks, which should already be included with most Xcode templates: 36 | 37 | * Foundation.framework 38 | * UIKit.framework 39 | * CoreGraphics.framework 40 | * QuartzCore.framework 41 | 42 | You will need LLVM 3.0 or later in order to build PNChart. 43 | 44 | DESC 45 | 46 | s.homepage = "https://github.com/kevinzhow/PNChart" 47 | s.screenshots = "https://camo.githubusercontent.com/e99c1bbab103c63efd561c4997a4bedb878bb2a2/68747470733a2f2f646c2e64726f70626f7875736572636f6e74656e742e636f6d2f752f313539393636322f706e63686172742e676966" 48 | 49 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 50 | # 51 | # Licensing your code is important. See http://choosealicense.com for more info. 52 | # CocoaPods will detect a license file if there is a named LICENSE* 53 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 54 | # 55 | 56 | s.license = { :type => "MIT", :file => "LICENSE" } 57 | 58 | 59 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 60 | # 61 | # Specify the authors of the library, with email addresses. Email addresses 62 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 63 | # accepts just a name if you'd rather not provide an email address. 64 | # 65 | # Specify a social_media_url where others can refer to, for example a twitter 66 | # profile URL. 67 | # 68 | 69 | s.author = { "kevinzhow" => "kevinchou.c@gmail.com" } 70 | # Or just: s.author = "kevinzhow" 71 | # s.authors = { "kevinzhow" => "kevinchou.c@gmail.com" } 72 | # s.social_media_url = "http://twitter.com/kevinzhow" 73 | 74 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 75 | # 76 | # If this Pod runs only on iOS or OS X, then specify the platform and 77 | # the deployment target. You can optionally include the target after the platform. 78 | # 79 | 80 | s.platform = :ios, "7.0" 81 | 82 | # When using multiple platforms 83 | # s.ios.deployment_target = "5.0" 84 | # s.osx.deployment_target = "10.7" 85 | 86 | 87 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 88 | # 89 | # Specify the location from where the source should be retrieved. 90 | # Supports git, hg, bzr, svn and HTTP. 91 | # 92 | 93 | s.source = { :git => "https://github.com/kevinzhow/PNChart.git", :tag => s.version } 94 | 95 | 96 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 97 | # 98 | # CocoaPods is smart about how it includes source code. For source files 99 | # giving a folder will include any h, m, mm, c & cpp files. For header 100 | # files it will include any header in the folder. 101 | # Not including the public_header_files will make all headers public. 102 | # 103 | 104 | s.source_files = "PNChart", "PNChart/**/*.{h,m}" 105 | #s.exclude_files = "Classes/Exclude" 106 | 107 | s.public_header_files = "PNChart/**/*.h" 108 | 109 | 110 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 111 | # 112 | # A list of resources included with the Pod. These are copied into the 113 | # target bundle with a build phase script. Anything else will be cleaned. 114 | # You can preserve files from being cleaned, please don't preserve 115 | # non-essential files like tests, examples and documentation. 116 | # 117 | 118 | # s.resource = "icon.png" 119 | # s.resources = "Resources/*.png" 120 | 121 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 122 | 123 | 124 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 125 | # 126 | # Link your library with frameworks, or libraries. Libraries do not include 127 | # the lib prefix of their name. 128 | # 129 | 130 | s.frameworks = 'CoreGraphics', 'UIKit', 'Foundation', 'QuartzCore' 131 | # s.frameworks = "SomeFramework", "AnotherFramework" 132 | 133 | # s.library = "iconv" 134 | # s.libraries = "iconv", "xml2" 135 | 136 | 137 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 138 | # 139 | # If your library depends on compiler flags you can set them in the xcconfig hash 140 | # where they will only apply to your library. If you depend on other Podspecs 141 | # you can include multiple dependencies to ensure it works. 142 | 143 | s.requires_arc = true 144 | 145 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 146 | s.dependency 'UICountingLabel', '~> 1.4.1' 147 | 148 | end 149 | -------------------------------------------------------------------------------- /UICountingLabel.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "UICountingLabel.h" 4 | 5 | #if !__has_feature(objc_arc) 6 | #error UICountingLabel is ARC only. Either turn on ARC for the project or use -fobjc-arc flag 7 | #endif 8 | 9 | #pragma mark - UILabelCounter 10 | 11 | #ifndef kUILabelCounterRate 12 | #define kUILabelCounterRate 3.0 13 | #endif 14 | 15 | @protocol UILabelCounter 16 | 17 | -(CGFloat)update:(CGFloat)t; 18 | 19 | @end 20 | 21 | @interface UILabelCounterLinear : NSObject 22 | 23 | @end 24 | 25 | @interface UILabelCounterEaseIn : NSObject 26 | 27 | @end 28 | 29 | @interface UILabelCounterEaseOut : NSObject 30 | 31 | @end 32 | 33 | @interface UILabelCounterEaseInOut : NSObject 34 | 35 | @end 36 | 37 | @interface UILabelCounterEaseInBounce : NSObject 38 | 39 | @end 40 | 41 | @interface UILabelCounterEaseOutBounce : NSObject 42 | 43 | @end 44 | 45 | @implementation UILabelCounterLinear 46 | 47 | -(CGFloat)update:(CGFloat)t 48 | { 49 | return t; 50 | } 51 | 52 | @end 53 | 54 | @implementation UILabelCounterEaseIn 55 | 56 | -(CGFloat)update:(CGFloat)t 57 | { 58 | return powf(t, kUILabelCounterRate); 59 | } 60 | 61 | @end 62 | 63 | @implementation UILabelCounterEaseOut 64 | 65 | -(CGFloat)update:(CGFloat)t{ 66 | return 1.0-powf((1.0-t), kUILabelCounterRate); 67 | } 68 | 69 | @end 70 | 71 | @implementation UILabelCounterEaseInOut 72 | 73 | -(CGFloat) update: (CGFloat) t 74 | { 75 | t *= 2; 76 | if (t < 1) 77 | return 0.5f * powf (t, kUILabelCounterRate); 78 | else 79 | return 0.5f * (2.0f - powf(2.0 - t, kUILabelCounterRate)); 80 | } 81 | 82 | @end 83 | 84 | @implementation UILabelCounterEaseInBounce 85 | 86 | -(CGFloat) update: (CGFloat) t { 87 | 88 | if (t < 4.0 / 11.0) { 89 | return 1.0 - (powf(11.0 / 4.0, 2) * powf(t, 2)) - t; 90 | } 91 | 92 | if (t < 8.0 / 11.0) { 93 | return 1.0 - (3.0 / 4.0 + powf(11.0 / 4.0, 2) * powf(t - 6.0 / 11.0, 2)) - t; 94 | } 95 | 96 | if (t < 10.0 / 11.0) { 97 | return 1.0 - (15.0 /16.0 + powf(11.0 / 4.0, 2) * powf(t - 9.0 / 11.0, 2)) - t; 98 | } 99 | 100 | return 1.0 - (63.0 / 64.0 + powf(11.0 / 4.0, 2) * powf(t - 21.0 / 22.0, 2)) - t; 101 | 102 | } 103 | 104 | @end 105 | 106 | @implementation UILabelCounterEaseOutBounce 107 | 108 | -(CGFloat) update: (CGFloat) t { 109 | 110 | if (t < 4.0 / 11.0) { 111 | return powf(11.0 / 4.0, 2) * powf(t, 2); 112 | } 113 | 114 | if (t < 8.0 / 11.0) { 115 | return 3.0 / 4.0 + powf(11.0 / 4.0, 2) * powf(t - 6.0 / 11.0, 2); 116 | } 117 | 118 | if (t < 10.0 / 11.0) { 119 | return 15.0 /16.0 + powf(11.0 / 4.0, 2) * powf(t - 9.0 / 11.0, 2); 120 | } 121 | 122 | return 63.0 / 64.0 + powf(11.0 / 4.0, 2) * powf(t - 21.0 / 22.0, 2); 123 | 124 | } 125 | 126 | @end 127 | 128 | #pragma mark - UICountingLabel 129 | 130 | @interface UICountingLabel () 131 | 132 | @property CGFloat startingValue; 133 | @property CGFloat destinationValue; 134 | @property NSTimeInterval progress; 135 | @property NSTimeInterval lastUpdate; 136 | @property NSTimeInterval totalTime; 137 | @property CGFloat easingRate; 138 | 139 | @property (nonatomic, strong) CADisplayLink *timer; 140 | @property (nonatomic, strong) id counter; 141 | 142 | @end 143 | 144 | @implementation UICountingLabel 145 | 146 | -(void)countFrom:(CGFloat)value to:(CGFloat)endValue { 147 | 148 | if (self.animationDuration == 0.0f) { 149 | self.animationDuration = 2.0f; 150 | } 151 | 152 | [self countFrom:value to:endValue withDuration:self.animationDuration]; 153 | } 154 | 155 | -(void)countFrom:(CGFloat)startValue to:(CGFloat)endValue withDuration:(NSTimeInterval)duration { 156 | 157 | self.startingValue = startValue; 158 | self.destinationValue = endValue; 159 | 160 | // remove any (possible) old timers 161 | [self.timer invalidate]; 162 | self.timer = nil; 163 | 164 | if(self.format == nil) { 165 | self.format = @"%f"; 166 | } 167 | if (duration == 0.0) { 168 | // No animation 169 | [self setTextValue:endValue]; 170 | [self runCompletionBlock]; 171 | return; 172 | } 173 | 174 | self.easingRate = 3.0f; 175 | self.progress = 0; 176 | self.totalTime = duration; 177 | self.lastUpdate = [NSDate timeIntervalSinceReferenceDate]; 178 | 179 | switch(self.method) 180 | { 181 | case UILabelCountingMethodLinear: 182 | self.counter = [[UILabelCounterLinear alloc] init]; 183 | break; 184 | case UILabelCountingMethodEaseIn: 185 | self.counter = [[UILabelCounterEaseIn alloc] init]; 186 | break; 187 | case UILabelCountingMethodEaseOut: 188 | self.counter = [[UILabelCounterEaseOut alloc] init]; 189 | break; 190 | case UILabelCountingMethodEaseInOut: 191 | self.counter = [[UILabelCounterEaseInOut alloc] init]; 192 | break; 193 | case UILabelCountingMethodEaseOutBounce: 194 | self.counter = [[UILabelCounterEaseOutBounce alloc] init]; 195 | break; 196 | case UILabelCountingMethodEaseInBounce: 197 | self.counter = [[UILabelCounterEaseInBounce alloc] init]; 198 | break; 199 | } 200 | 201 | CADisplayLink *timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateValue:)]; 202 | timer.frameInterval = 2; 203 | [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; 204 | [timer addToRunLoop:[NSRunLoop mainRunLoop] forMode:UITrackingRunLoopMode]; 205 | self.timer = timer; 206 | } 207 | 208 | - (void)countFromCurrentValueTo:(CGFloat)endValue { 209 | [self countFrom:[self currentValue] to:endValue]; 210 | } 211 | 212 | - (void)countFromCurrentValueTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration { 213 | [self countFrom:[self currentValue] to:endValue withDuration:duration]; 214 | } 215 | 216 | - (void)countFromZeroTo:(CGFloat)endValue { 217 | [self countFrom:0.0f to:endValue]; 218 | } 219 | 220 | - (void)countFromZeroTo:(CGFloat)endValue withDuration:(NSTimeInterval)duration { 221 | [self countFrom:0.0f to:endValue withDuration:duration]; 222 | } 223 | 224 | - (void)updateValue:(NSTimer *)timer { 225 | 226 | // update progress 227 | NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; 228 | self.progress += now - self.lastUpdate; 229 | self.lastUpdate = now; 230 | 231 | if (self.progress >= self.totalTime) { 232 | [self.timer invalidate]; 233 | self.timer = nil; 234 | self.progress = self.totalTime; 235 | } 236 | 237 | [self setTextValue:[self currentValue]]; 238 | 239 | if (self.progress == self.totalTime) { 240 | [self runCompletionBlock]; 241 | } 242 | } 243 | 244 | - (void)setTextValue:(CGFloat)value 245 | { 246 | if (self.attributedFormatBlock != nil) { 247 | self.attributedText = self.attributedFormatBlock(value); 248 | } 249 | else if(self.formatBlock != nil) 250 | { 251 | self.text = self.formatBlock(value); 252 | } 253 | else 254 | { 255 | // check if counting with ints - cast to int 256 | if([self.format rangeOfString:@"%(.*)d" options:NSRegularExpressionSearch].location != NSNotFound || [self.format rangeOfString:@"%(.*)i"].location != NSNotFound ) 257 | { 258 | self.text = [NSString stringWithFormat:self.format,(int)value]; 259 | } 260 | else 261 | { 262 | self.text = [NSString stringWithFormat:self.format,value]; 263 | } 264 | } 265 | } 266 | 267 | - (void)setFormat:(NSString *)format { 268 | _format = format; 269 | // update label with new format 270 | [self setTextValue:self.currentValue]; 271 | } 272 | 273 | - (void)runCompletionBlock { 274 | 275 | if (self.completionBlock) { 276 | self.completionBlock(); 277 | self.completionBlock = nil; 278 | } 279 | } 280 | 281 | - (CGFloat)currentValue { 282 | 283 | if (self.progress >= self.totalTime) { 284 | return self.destinationValue; 285 | } 286 | 287 | CGFloat percent = self.progress / self.totalTime; 288 | CGFloat updateVal = [self.counter update:percent]; 289 | return self.startingValue + (updateVal * (self.destinationValue - self.startingValue)); 290 | } 291 | 292 | @end 293 | -------------------------------------------------------------------------------- /PNChart/PNBar.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNBar.m 3 | // PNChartDemo 4 | // 5 | // Created by kevin on 11/7/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNBar.h" 10 | #import "PNColor.h" 11 | #import 12 | 13 | @interface PNBar () 14 | 15 | @property (nonatomic) float copyGrade; 16 | 17 | @end 18 | 19 | @implementation PNBar 20 | 21 | - (id)initWithFrame:(CGRect)frame 22 | { 23 | self = [super initWithFrame:frame]; 24 | 25 | if (self) { 26 | _chartLine = [CAShapeLayer layer]; 27 | _chartLine.lineCap = kCALineCapButt; 28 | _chartLine.fillColor = [[UIColor whiteColor] CGColor]; 29 | _chartLine.lineWidth = self.frame.size.width; 30 | _chartLine.strokeEnd = 0.0; 31 | self.clipsToBounds = YES; 32 | [self.layer addSublayer:_chartLine]; 33 | self.barRadius = 2.0; 34 | } 35 | 36 | return self; 37 | } 38 | 39 | -(void)setBarRadius:(CGFloat)barRadius 40 | { 41 | _barRadius = barRadius; 42 | self.layer.cornerRadius = _barRadius; 43 | } 44 | 45 | 46 | - (void)setGrade:(float)grade 47 | { 48 | _copyGrade = grade; 49 | CGFloat startPosY = (1 - grade) * self.frame.size.height; 50 | 51 | UIBezierPath *progressline = [UIBezierPath bezierPath]; 52 | 53 | [progressline moveToPoint:CGPointMake(self.frame.size.width / 2.0, self.frame.size.height)]; 54 | [progressline addLineToPoint:CGPointMake(self.frame.size.width / 2.0, startPosY)]; 55 | 56 | [progressline setLineWidth:1.0]; 57 | [progressline setLineCapStyle:kCGLineCapSquare]; 58 | [self addAnimationIfNeededWithProgressLine:progressline]; 59 | 60 | 61 | if (_barColor) { 62 | _chartLine.strokeColor = [_barColor CGColor]; 63 | } 64 | else { 65 | _chartLine.strokeColor = [PNGreen CGColor]; 66 | } 67 | 68 | if (_grade) { 69 | 70 | _chartLine.path = progressline.CGPath; 71 | 72 | if (_barColorGradientStart) { 73 | 74 | // Add gradient 75 | self.gradientMask.path = progressline.CGPath; 76 | 77 | CABasicAnimation* opacityAnimation = [self fadeAnimation]; 78 | [self.textLayer addAnimation:opacityAnimation forKey:nil]; 79 | 80 | } 81 | 82 | }else{ 83 | _chartLine.strokeEnd = 1.0; 84 | 85 | _chartLine.path = progressline.CGPath; 86 | // Check if user wants to add a gradient from the start color to the bar color 87 | if (_barColorGradientStart) { 88 | 89 | // Add gradient 90 | self.gradientMask = [CAShapeLayer layer]; 91 | self.gradientMask.fillColor = [[UIColor clearColor] CGColor]; 92 | self.gradientMask.strokeColor = [[UIColor blackColor] CGColor]; 93 | self.gradientMask.lineWidth = self.frame.size.width; 94 | self.gradientMask.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height); 95 | self.gradientMask.path = progressline.CGPath; 96 | 97 | CAGradientLayer *gradientLayer = [CAGradientLayer layer]; 98 | gradientLayer.startPoint = CGPointMake(0.0,0.0); 99 | gradientLayer.endPoint = CGPointMake(1.0 ,0.0); 100 | gradientLayer.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height); 101 | UIColor *middleColor = [UIColor colorWithWhite:255/255 alpha:0.8]; 102 | NSArray *colors = @[ 103 | (__bridge id)self.barColor.CGColor, 104 | (__bridge id)middleColor.CGColor, 105 | (__bridge id)self.barColor.CGColor 106 | ]; 107 | gradientLayer.colors = colors; 108 | 109 | [gradientLayer setMask:self.gradientMask]; 110 | 111 | [_chartLine addSublayer:gradientLayer]; 112 | 113 | self.gradientMask.strokeEnd = 1.0; 114 | 115 | CABasicAnimation* opacityAnimation = [self fadeAnimation]; 116 | [self.textLayer addAnimation:opacityAnimation forKey:nil]; 117 | } 118 | } 119 | 120 | _grade = grade; 121 | 122 | } 123 | 124 | 125 | - (void)rollBack 126 | { 127 | [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations: ^{ 128 | _chartLine.strokeColor = [UIColor clearColor].CGColor; 129 | } completion:nil]; 130 | } 131 | 132 | - (void)setBarColorGradientStart:(UIColor *)barColorGradientStart 133 | { 134 | // Set gradient color, remove any existing sublayer first 135 | for (CALayer *sublayer in [_chartLine sublayers]) { 136 | [sublayer removeFromSuperlayer]; 137 | } 138 | _barColorGradientStart = barColorGradientStart; 139 | 140 | [self setGrade:_grade]; 141 | 142 | } 143 | 144 | // Only override drawRect: if you perform custom drawing. 145 | // An empty implementation adversely affects performance during animation. 146 | - (void)drawRect:(CGRect)rect 147 | { 148 | CGContextRef context = UIGraphicsGetCurrentContext(); 149 | 150 | CGContextSetFillColorWithColor(context, self.backgroundColor.CGColor); 151 | CGContextFillRect(context, rect); 152 | } 153 | 154 | 155 | // add number display on the top of bar 156 | -(CGPathRef)gradePath:(CGRect)rect 157 | { 158 | return nil; 159 | } 160 | 161 | -(CATextLayer*)textLayer 162 | { 163 | if (!_textLayer) { 164 | _textLayer = [[CATextLayer alloc]init]; 165 | [_textLayer setString:@"0"]; 166 | [_textLayer setAlignmentMode:kCAAlignmentCenter]; 167 | [_textLayer setForegroundColor:[_labelTextColor CGColor]]; 168 | _textLayer.hidden = YES; 169 | 170 | } 171 | 172 | return _textLayer; 173 | } 174 | 175 | - (void) setLabelTextColor:(UIColor *)labelTextColor { 176 | _labelTextColor = labelTextColor; 177 | [_textLayer setForegroundColor:[_labelTextColor CGColor]]; 178 | } 179 | 180 | -(void)setGradeFrame:(CGFloat)grade startPosY:(CGFloat)startPosY 181 | { 182 | CGFloat textheigt = self.bounds.size.height*self.grade; 183 | 184 | CGFloat topSpace = self.bounds.size.height * (1-self.grade); 185 | CGFloat textWidth = self.bounds.size.width; 186 | 187 | [_chartLine addSublayer:self.textLayer]; 188 | [self.textLayer setFontSize:18.0]; 189 | 190 | [self.textLayer setString:[[NSString alloc]initWithFormat:@"%0.f",grade*self.maxDivisor]]; 191 | 192 | CGSize size = CGSizeMake(320,2000); //设置一个行高上限 193 | NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:18.0]}; 194 | size = [self.textLayer.string boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size; 195 | float verticalY ; 196 | 197 | if (size.height>=textheigt) { 198 | 199 | verticalY = topSpace - size.height; 200 | } else { 201 | verticalY = topSpace + (textheigt-size.height)/2.0; 202 | } 203 | 204 | [self.textLayer setFrame:CGRectMake((textWidth-size.width)/2.0,verticalY, size.width,size.height)]; 205 | self.textLayer.contentsScale = [UIScreen mainScreen].scale; 206 | 207 | } 208 | 209 | - (void)setIsShowNumber:(BOOL)isShowNumber{ 210 | if (isShowNumber) { 211 | self.textLayer.hidden = NO; 212 | [self setGradeFrame:_copyGrade startPosY:0]; 213 | }else{ 214 | self.textLayer.hidden = YES; 215 | } 216 | } 217 | - (void)setIsNegative:(BOOL)isNegative{ 218 | if (isNegative) { 219 | [self.textLayer setString:[[NSString alloc]initWithFormat:@"- %1.f",_grade*self.maxDivisor]]; 220 | 221 | CGSize size = CGSizeMake(320,2000); //设置一个行高上限 222 | NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:18.0]}; 223 | size = [self.textLayer.string boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size; 224 | CGRect frame = self.textLayer.frame; 225 | frame.origin.x = (self.bounds.size.width - size.width)/2.0; 226 | frame.size = size; 227 | self.textLayer.frame = frame; 228 | 229 | [self addRotationAnimationIfNeeded]; 230 | } 231 | } 232 | 233 | -(CABasicAnimation*)fadeAnimation 234 | { 235 | CABasicAnimation* fadeAnimation = nil; 236 | if (self.displayAnimated) { 237 | fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 238 | fadeAnimation.fromValue = [NSNumber numberWithFloat:0.0]; 239 | fadeAnimation.toValue = [NSNumber numberWithFloat:1.0]; 240 | fadeAnimation.duration = 2.0; 241 | } 242 | return fadeAnimation; 243 | } 244 | 245 | -(void)addAnimationIfNeededWithProgressLine:(UIBezierPath *)progressline 246 | { 247 | if (self.displayAnimated) { 248 | CABasicAnimation *pathAnimation = nil; 249 | 250 | if (_grade) { 251 | pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"]; 252 | pathAnimation.fromValue = (id)_chartLine.path; 253 | pathAnimation.toValue = (id)[progressline CGPath]; 254 | pathAnimation.duration = 0.5f; 255 | pathAnimation.autoreverses = NO; 256 | pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 257 | [_chartLine addAnimation:pathAnimation forKey:@"animationKey"]; 258 | } 259 | else { 260 | pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 261 | pathAnimation.duration = 1.0; 262 | pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 263 | pathAnimation.fromValue = @0.0f; 264 | pathAnimation.toValue = @1.0f; 265 | [_chartLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; 266 | } 267 | 268 | [self.gradientMask addAnimation:pathAnimation forKey:@"animationKey"]; 269 | } 270 | } 271 | 272 | - (void)addRotationAnimationIfNeeded 273 | { 274 | if (self.displayAnimated) { 275 | CABasicAnimation* rotationAnimation; 276 | rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; 277 | rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI]; 278 | [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 279 | rotationAnimation.duration = 0.1; 280 | rotationAnimation.repeatCount = 0;//你可以设置到最大的整数值 281 | rotationAnimation.cumulative = NO; 282 | rotationAnimation.removedOnCompletion = NO; 283 | rotationAnimation.fillMode = kCAFillModeForwards; 284 | [self.textLayer addAnimation:rotationAnimation forKey:@"Rotation"]; 285 | } 286 | } 287 | 288 | @end 289 | -------------------------------------------------------------------------------- /PNChart/PNCircleChart.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNCircleChart.m 3 | // PNChartDemo 4 | // 5 | // Created by kevinzhow on 13-11-30. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNChart/PNCircleChart.h" 10 | 11 | @implementation PNCircleChart 12 | 13 | - (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise { 14 | 15 | return [self initWithFrame:frame 16 | total:total 17 | current:current 18 | clockwise:clockwise 19 | shadow:NO 20 | shadowColor:[UIColor clearColor] 21 | displayCountingLabel:YES 22 | overrideLineWidth:@8.0f]; 23 | 24 | } 25 | 26 | - (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise shadow:(BOOL)hasBackgroundShadow shadowColor:(UIColor *)backgroundShadowColor { 27 | 28 | return [self initWithFrame:frame 29 | total:total 30 | current:current 31 | clockwise:clockwise 32 | shadow:shadow 33 | shadowColor:backgroundShadowColor 34 | displayCountingLabel:YES 35 | overrideLineWidth:@8.0f]; 36 | 37 | } 38 | 39 | - (id)initWithFrame:(CGRect)frame total:(NSNumber *)total current:(NSNumber *)current clockwise:(BOOL)clockwise shadow:(BOOL)hasBackgroundShadow shadowColor:(UIColor *)backgroundShadowColor displayCountingLabel:(BOOL)displayCountingLabel { 40 | 41 | return [self initWithFrame:frame 42 | total:total 43 | current:current 44 | clockwise:clockwise 45 | shadow:shadow 46 | shadowColor:backgroundShadowColor 47 | displayCountingLabel:displayCountingLabel 48 | overrideLineWidth:@8.0f]; 49 | 50 | } 51 | 52 | - (id)initWithFrame:(CGRect)frame 53 | total:(NSNumber *)total 54 | current:(NSNumber *)current 55 | clockwise:(BOOL)clockwise 56 | shadow:(BOOL)hasBackgroundShadow 57 | shadowColor:(UIColor *)backgroundShadowColor 58 | displayCountingLabel:(BOOL)displayCountingLabel 59 | overrideLineWidth:(NSNumber *)overrideLineWidth 60 | { 61 | self = [super initWithFrame:frame]; 62 | 63 | if (self) { 64 | _total = total; 65 | _current = current; 66 | _strokeColor = PNFreshGreen; 67 | _duration = 1.0; 68 | _chartType = PNChartFormatTypePercent; 69 | _displayAnimated = YES; 70 | 71 | _displayCountingLabel = displayCountingLabel; 72 | 73 | CGFloat startAngle = clockwise ? -90.0f : 270.0f; 74 | CGFloat endAngle = clockwise ? -90.01f : 270.01f; 75 | 76 | _lineWidth = overrideLineWidth; 77 | 78 | UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.frame.size.width/2.0f, self.frame.size.height/2.0f) 79 | radius:(self.frame.size.height * 0.5) - ([_lineWidth floatValue]/2.0f) 80 | startAngle:DEGREES_TO_RADIANS(startAngle) 81 | endAngle:DEGREES_TO_RADIANS(endAngle) 82 | clockwise:clockwise]; 83 | 84 | _circle = [CAShapeLayer layer]; 85 | _circle.path = circlePath.CGPath; 86 | _circle.lineCap = kCALineCapRound; 87 | _circle.fillColor = [UIColor clearColor].CGColor; 88 | _circle.lineWidth = [_lineWidth floatValue]; 89 | _circle.zPosition = 1; 90 | 91 | _circleBackground = [CAShapeLayer layer]; 92 | _circleBackground.path = circlePath.CGPath; 93 | _circleBackground.lineCap = kCALineCapRound; 94 | _circleBackground.fillColor = [UIColor clearColor].CGColor; 95 | _circleBackground.lineWidth = [_lineWidth floatValue]; 96 | _circleBackground.strokeColor = (hasBackgroundShadow ? backgroundShadowColor.CGColor : [UIColor clearColor].CGColor); 97 | _circleBackground.strokeEnd = 1.0; 98 | _circleBackground.zPosition = -1; 99 | 100 | [self.layer addSublayer:_circle]; 101 | [self.layer addSublayer:_circleBackground]; 102 | 103 | _countingLabel = [[UICountingLabel alloc] initWithFrame:CGRectMake(0, 0, 100.0, 50.0)]; 104 | [_countingLabel setTextAlignment:NSTextAlignmentCenter]; 105 | [_countingLabel setFont:[UIFont boldSystemFontOfSize:16.0f]]; 106 | [_countingLabel setTextColor:[UIColor grayColor]]; 107 | [_countingLabel setBackgroundColor:[UIColor clearColor]]; 108 | [_countingLabel setCenter:CGPointMake(self.frame.size.width/2.0f, self.frame.size.height/2.0f)]; 109 | _countingLabel.method = UILabelCountingMethodEaseInOut; 110 | if (_displayCountingLabel) { 111 | [self addSubview:_countingLabel]; 112 | } 113 | } 114 | 115 | return self; 116 | } 117 | 118 | 119 | - (void)strokeChart 120 | { 121 | // Add counting label 122 | 123 | if (_displayCountingLabel) { 124 | NSString *format; 125 | switch (self.chartType) { 126 | case PNChartFormatTypePercent: 127 | format = @"%d%%"; 128 | break; 129 | case PNChartFormatTypeDollar: 130 | format = @"$%d"; 131 | break; 132 | case PNChartFormatTypeDecimal: 133 | format = @"%.1f"; 134 | break; 135 | case PNChartFormatTypeDecimalTwoPlaces: 136 | format = @"%.2f"; 137 | break; 138 | case PNChartFormatTypeNone: 139 | default: 140 | format = @"%d"; 141 | break; 142 | } 143 | self.countingLabel.format = format; 144 | [self addSubview:self.countingLabel]; 145 | } 146 | 147 | 148 | // Add circle params 149 | 150 | _circle.lineWidth = [_lineWidth floatValue]; 151 | _circleBackground.lineWidth = [_lineWidth floatValue]; 152 | _circleBackground.strokeEnd = 1.0; 153 | _circle.strokeColor = _strokeColor.CGColor; 154 | _circle.strokeEnd = [_current floatValue] / [_total floatValue]; 155 | 156 | // Check if user wants to add a gradient from the start color to the bar color 157 | if (_strokeColorGradientStart) { 158 | 159 | // Add gradient 160 | self.gradientMask = [CAShapeLayer layer]; 161 | self.gradientMask.fillColor = [[UIColor clearColor] CGColor]; 162 | self.gradientMask.strokeColor = [[UIColor blackColor] CGColor]; 163 | self.gradientMask.lineWidth = _circle.lineWidth; 164 | self.gradientMask.lineCap = kCALineCapRound; 165 | CGRect gradientFrame = CGRectMake(0, 0, 2*self.bounds.size.width, 2*self.bounds.size.height); 166 | self.gradientMask.frame = gradientFrame; 167 | self.gradientMask.path = _circle.path; 168 | 169 | CAGradientLayer *gradientLayer = [CAGradientLayer layer]; 170 | gradientLayer.startPoint = CGPointMake(0.5,1.0); 171 | gradientLayer.endPoint = CGPointMake(0.5,0.0); 172 | gradientLayer.frame = gradientFrame; 173 | UIColor *endColor = (_strokeColor ? _strokeColor : [UIColor greenColor]); 174 | NSArray *colors = @[ 175 | (id)endColor.CGColor, 176 | (id)_strokeColorGradientStart.CGColor 177 | ]; 178 | gradientLayer.colors = colors; 179 | 180 | [gradientLayer setMask:self.gradientMask]; 181 | 182 | [_circle addSublayer:gradientLayer]; 183 | 184 | self.gradientMask.strokeEnd = [_current floatValue] / [_total floatValue]; 185 | } 186 | 187 | [self addAnimationIfNeeded]; 188 | } 189 | 190 | 191 | 192 | - (void)growChartByAmount:(NSNumber *)growAmount 193 | { 194 | NSNumber *updatedValue = [NSNumber numberWithFloat:[_current floatValue] + [growAmount floatValue]]; 195 | 196 | // Add animation 197 | [self updateChartByCurrent:updatedValue]; 198 | } 199 | 200 | 201 | -(void)updateChartByCurrent:(NSNumber *)current{ 202 | 203 | [self updateChartByCurrent:current 204 | byTotal:_total]; 205 | 206 | } 207 | 208 | -(void)updateChartByCurrent:(NSNumber *)current byTotal:(NSNumber *)total { 209 | double totalPercentageValue = [current floatValue]/([total floatValue]/100.0); 210 | 211 | if (_strokeColorGradientStart) { 212 | self.gradientMask.strokeEnd = _circle.strokeEnd; 213 | } 214 | 215 | // Add animation 216 | if (self.displayAnimated) { 217 | CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 218 | pathAnimation.duration = self.duration; 219 | pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 220 | pathAnimation.fromValue = @([_current floatValue] / [_total floatValue]); 221 | pathAnimation.toValue = @([current floatValue] / [total floatValue]); 222 | 223 | if (_strokeColorGradientStart) { 224 | [self.gradientMask addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; 225 | } 226 | [_circle addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; 227 | 228 | if (_displayCountingLabel) { 229 | [self.countingLabel countFrom:fmin([_current floatValue], [_total floatValue]) to:totalPercentageValue withDuration:self.duration]; 230 | } 231 | 232 | } 233 | else if (_displayCountingLabel) { 234 | [self.countingLabel countFrom:totalPercentageValue to:totalPercentageValue withDuration:self.duration]; 235 | } 236 | 237 | _circle.strokeEnd = [current floatValue] / [total floatValue]; 238 | _current = current; 239 | _total = total; 240 | } 241 | 242 | - (void)addAnimationIfNeeded 243 | { 244 | double percentageValue = (_current.floatValue / _total.floatValue) * 100.0f; 245 | 246 | if (self.displayAnimated) { 247 | CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 248 | pathAnimation.duration = self.duration; 249 | pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 250 | pathAnimation.fromValue = @(0.0f); 251 | pathAnimation.toValue = @([_current floatValue] / [_total floatValue]); 252 | [_circle addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; 253 | if(_displayCountingLabel) 254 | { 255 | [_countingLabel countFrom:0 to:percentageValue withDuration:self.duration]; 256 | } 257 | if (self.gradientMask && _strokeColorGradientStart) { 258 | [self.gradientMask addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; 259 | } 260 | } 261 | else { 262 | if (_displayCountingLabel) { 263 | [_countingLabel countFrom:percentageValue to:percentageValue withDuration:self.duration]; 264 | } 265 | } 266 | } 267 | 268 | @end 269 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PNChart 2 | 3 | [![Build Status](https://travis-ci.org/kevinzhow/PNChart.png?branch=master)](https://travis-ci.org/kevinzhow/PNChart) 4 | 5 | You can also find swift version at here https://github.com/kevinzhow/PNChart-Swift 6 | 7 | A simple and beautiful chart lib with **animation** used in [Piner](https://itunes.apple.com/us/app/piner/id637706410) and [CoinsMan](https://itunes.apple.com/us/app/coinsman/id772163893) for iOS 8 | 9 | [![](https://s3.amazonaws.com/farshid.ghods.github/pnchart-gif.gif)](https://s3.amazonaws.com/farshid.ghods.github/pnchart-gif.gif) 10 | 11 | ## Requirements 12 | 13 | PNChart works on iOS 7.0+ and is compatible with ARC projects. 14 | If you need support for iOS 6, use PNChart <= 0.8.1. Note that 0.8.2 supports iOS 8.0+ only, 0.8.3 and newer supports iOS 7.0+. 15 | 16 | It depends on the following Apple frameworks, which should already be included with most Xcode templates: 17 | 18 | * Foundation.framework 19 | * UIKit.framework 20 | * CoreGraphics.framework 21 | * QuartzCore.framework 22 | 23 | You will need LLVM 3.0 or later in order to build PNChart. 24 | 25 | 26 | ## Usage 27 | 28 | ### Cocoapods 29 | 30 | [CocoaPods](http://cocoapods.org) is the recommended way to add PNChart to your project. 31 | 32 | 1. Add a pod entry for PNChart to your Podfile `pod 'PNChart'` 33 | 2. Install the pod(s) by running `pod install`. 34 | 3. Include PNChart wherever you need it with `#import "PNChart.h"`. 35 | 36 | 37 | ### Copy the PNChart folder to your project 38 | 39 | ```objective-c 40 | #import "PNChart.h" 41 | 42 | //For Line Chart 43 | PNLineChart * lineChart = [[PNLineChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 200.0)]; 44 | [lineChart setXLabels:@[@"SEP 1",@"SEP 2",@"SEP 3",@"SEP 4",@"SEP 5"]]; 45 | 46 | // Line Chart No.1 47 | NSArray * data01Array = @[@60.1, @160.1, @126.4, @262.2, @186.2]; 48 | PNLineChartData *data01 = [PNLineChartData new]; 49 | data01.color = PNFreshGreen; 50 | data01.itemCount = lineChart.xLabels.count; 51 | data01.getData = ^(NSUInteger index) { 52 | CGFloat yValue = [data01Array[index] floatValue]; 53 | return [PNLineChartDataItem dataItemWithY:yValue]; 54 | }; 55 | // Line Chart No.2 56 | NSArray * data02Array = @[@20.1, @180.1, @26.4, @202.2, @126.2]; 57 | PNLineChartData *data02 = [PNLineChartData new]; 58 | data02.color = PNTwitterColor; 59 | data02.itemCount = lineChart.xLabels.count; 60 | data02.getData = ^(NSUInteger index) { 61 | CGFloat yValue = [data02Array[index] floatValue]; 62 | return [PNLineChartDataItem dataItemWithY:yValue]; 63 | }; 64 | 65 | lineChart.chartData = @[data01, data02]; 66 | [lineChart strokeChart]; 67 | ``` 68 | 69 | 70 | You can choose to show smooth lines. 71 | 72 | ```objective-c 73 | lineChart.showSmoothLines = YES; 74 | ``` 75 | 76 | [![](https://s3.amazonaws.com/farshid.ghods.github/pnchart-linechart-smooth.png)](https://s3.amazonaws.com/farshid.ghods.github/pnchart-linechart-smooth.png) 77 | 78 | 79 | You can set different colors for the same PNLineChartData item. for instance you can use "color" red for values less than 50 and use purple for values greater than 150. 80 | 81 | 82 | [![](https://s3.amazonaws.com/farshid.ghods.github/pnchart-rangecolors-2.png)](https://s3.amazonaws.com/farshid.ghods.github/pnchart-rangecolors-2.png) 83 | 84 | 85 | ```objective-c 86 | lineChartData.rangeColors = @[ 87 | [[PNLineChartColorRange alloc] initWithRange:NSMakeRange(10, 30) color:[UIColor redColor]], 88 | [[PNLineChartColorRange alloc] initWithRange:NSMakeRange(100, 200) color:[UIColor purpleColor]] 89 | ]; 90 | ``` 91 | 92 | ```objective-c 93 | #import "PNChart.h" 94 | 95 | //For BarC hart 96 | PNBarChart * barChart = [[PNBarChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 200.0)]; 97 | [barChart setXLabels:@[@"SEP 1",@"SEP 2",@"SEP 3",@"SEP 4",@"SEP 5"]]; 98 | [barChart setYValues:@[@1, @10, @2, @6, @3]]; 99 | [barChart strokeChart]; 100 | 101 | ``` 102 | 103 | ```objective-c 104 | #import "PNChart.h" 105 | 106 | //For Circle Chart 107 | 108 | PNCircleChart * circleChart = [[PNCircleChart alloc] initWithFrame:CGRectMake(0, 80.0, SCREEN_WIDTH, 100.0) total:[NSNumber numberWithInt:100] current:[NSNumber numberWithInt:60] clockwise:NO shadow:NO]; 109 | circleChart.backgroundColor = [UIColor clearColor]; 110 | [circleChart setStrokeColor:PNGreen]; 111 | [circleChart strokeChart]; 112 | 113 | ``` 114 | 115 | 116 | ```objective-c 117 | # import "PNChart.h" 118 | //For Pie Chart 119 | NSArray *items = @[[PNPieChartDataItem dataItemWithValue:10 color:PNRed], 120 | [PNPieChartDataItem dataItemWithValue:20 color:PNBlue description:@"WWDC"], 121 | [PNPieChartDataItem dataItemWithValue:40 color:PNGreen description:@"GOOL I/O"], 122 | ]; 123 | 124 | 125 | 126 | PNPieChart *pieChart = [[PNPieChart alloc] initWithFrame:CGRectMake(40.0, 155.0, 240.0, 240.0) items:items]; 127 | pieChart.descriptionTextColor = [UIColor whiteColor]; 128 | pieChart.descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:14.0]; 129 | [pieChart strokeChart]; 130 | ``` 131 | 132 | ```objective-c 133 | # import "PNChart.h" 134 | //For Scatter Chart 135 | 136 | PNScatterChart *scatterChart = [[PNScatterChart alloc] initWithFrame:CGRectMake(SCREEN_WIDTH /6.0 - 30, 135, 280, 200)]; 137 | [scatterChart setAxisXWithMinimumValue:20 andMaxValue:100 toTicks:6]; 138 | [scatterChart setAxisYWithMinimumValue:30 andMaxValue:50 toTicks:5]; 139 | 140 | NSArray * data01Array = [self randomSetOfObjects]; 141 | PNScatterChartData *data01 = [PNScatterChartData new]; 142 | data01.strokeColor = PNGreen; 143 | data01.fillColor = PNFreshGreen; 144 | data01.size = 2; 145 | data01.itemCount = [[data01Array objectAtIndex:0] count]; 146 | data01.inflexionPointStyle = PNScatterChartPointStyleCircle; 147 | __block NSMutableArray *XAr1 = [NSMutableArray arrayWithArray:[data01Array objectAtIndex:0]]; 148 | __block NSMutableArray *YAr1 = [NSMutableArray arrayWithArray:[data01Array objectAtIndex:1]]; 149 | data01.getData = ^(NSUInteger index) { 150 | CGFloat xValue = [[XAr1 objectAtIndex:index] floatValue]; 151 | CGFloat yValue = [[YAr1 objectAtIndex:index] floatValue]; 152 | return [PNScatterChartDataItem dataItemWithX:xValue AndWithY:yValue]; 153 | }; 154 | 155 | [scatterChart setup]; 156 | self.scatterChart.chartData = @[data01]; 157 | /*** 158 | this is for drawing line to compare 159 | CGPoint start = CGPointMake(20, 35); 160 | CGPoint end = CGPointMake(80, 45); 161 | [scatterChart drawLineFromPoint:start ToPoint:end WithLineWith:2 AndWithColor:PNBlack]; 162 | ***/ 163 | scatterChart.delegate = self; 164 | ``` 165 | 166 | #### Legend 167 | 168 | Legend has been added to PNChart for Line and Pie Charts. Legend items position can be stacked or in series. 169 | 170 | ```objective-c 171 | #import "PNChart.h" 172 | 173 | //For Line Chart 174 | 175 | //Add Line Titles for the Legend 176 | data01.dataTitle = @"Alpha"; 177 | data02.dataTitle = @"Beta Beta Beta Beta"; 178 | 179 | //Build the legend 180 | self.lineChart.legendStyle = PNLegendItemStyleSerial; 181 | UIView *legend = [self.lineChart getLegendWithMaxWidth:320]; 182 | 183 | //Move legend to the desired position and add to view 184 | [legend setFrame:CGRectMake(100, 400, legend.frame.size.width, legend.frame.size.height)]; 185 | [self.view addSubview:legend]; 186 | 187 | 188 | //For Pie Chart 189 | 190 | //Build the legend 191 | self.pieChart.legendStyle = PNLegendItemStyleStacked; 192 | UIView *legend = [self.pieChart getLegendWithMaxWidth:200]; 193 | 194 | //Move legend to the desired position and add to view 195 | [legend setFrame:CGRectMake(130, 350, legend.frame.size.width, legend.frame.size.height)]; 196 | [self.view addSubview:legend]; 197 | ``` 198 | 199 | #### Grid Lines 200 | 201 | Grid lines have been added to PNChart for Line Chart. 202 | 203 | ```objective-c 204 | lineChart.showYGridLines = YES; 205 | lineChart.yGridLinesColor = [UIColor grayColor]; 206 | ``` 207 | 208 | [![](https://s3.amazonaws.com/farshid.ghods.github/pnchart-gridline.png)](https://s3.amazonaws.com/farshid.ghods.github/pnchart-gridline.png) 209 | 210 | #### Update Value 211 | 212 | Now it's easy to update value in real time 213 | 214 | ```objective-c 215 | if ([self.title isEqualToString:@"Line Chart"]) { 216 | 217 | // Line Chart #1 218 | NSArray * data01Array = @[@(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300)]; 219 | PNLineChartData *data01 = [PNLineChartData new]; 220 | data01.color = PNFreshGreen; 221 | data01.itemCount = data01Array.count; 222 | data01.inflexionPointStyle = PNLineChartPointStyleTriangle; 223 | data01.getData = ^(NSUInteger index) { 224 | CGFloat yValue = [data01Array[index] floatValue]; 225 | return [PNLineChartDataItem dataItemWithY:yValue]; 226 | }; 227 | 228 | // Line Chart #2 229 | NSArray * data02Array = @[@(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300)]; 230 | PNLineChartData *data02 = [PNLineChartData new]; 231 | data02.color = PNTwitterColor; 232 | data02.itemCount = data02Array.count; 233 | data02.inflexionPointStyle = PNLineChartPointStyleSquare; 234 | data02.getData = ^(NSUInteger index) { 235 | CGFloat yValue = [data02Array[index] floatValue]; 236 | return [PNLineChartDataItem dataItemWithY:yValue]; 237 | }; 238 | 239 | [self.lineChart setXLabels:@[@"DEC 1",@"DEC 2",@"DEC 3",@"DEC 4",@"DEC 5",@"DEC 6",@"DEC 7"]]; 240 | [self.lineChart updateChartData:@[data01, data02]]; 241 | 242 | } 243 | else if ([self.title isEqualToString:@"Bar Chart"]) 244 | { 245 | [self.barChart setXLabels:@[@"Jan 1",@"Jan 2",@"Jan 3",@"Jan 4",@"Jan 5",@"Jan 6",@"Jan 7"]]; 246 | [self.barChart updateChartData:@[@(arc4random() % 30),@(arc4random() % 30),@(arc4random() % 30),@(arc4random() % 30),@(arc4random() % 30),@(arc4random() % 30),@(arc4random() % 30)]]; 247 | } 248 | else if ([self.title isEqualToString:@"Circle Chart"]) 249 | { 250 | [self.circleChart updateChartByCurrent:@(arc4random() % 100)]; 251 | } 252 | ``` 253 | 254 | #### Callback 255 | 256 | ```objective-c 257 | #import "PNChart.h" 258 | 259 | //For LineChart 260 | 261 | lineChart.delegate = self; 262 | 263 | 264 | ``` 265 | 266 | #### Animation 267 | 268 | Animation is enabled by default when drawing all charts. It can be disabled by setting `displayAnimation = NO`. 269 | 270 | ```objective-c 271 | #import "PNChart.h" 272 | 273 | //For LineChart 274 | 275 | lineChart.displayAnimation = NO; 276 | 277 | ``` 278 | 279 | 280 | 281 | ```objective-c 282 | 283 | //For DelegateMethod 284 | 285 | 286 | -(void)userClickedOnLineKeyPoint:(CGPoint)point lineIndex:(NSInteger)lineIndex pointIndex:(NSInteger)pointIndex{ 287 | NSLog(@"Click Key on line %f, %f line index is %d and point index is %d",point.x, point.y,(int)lineIndex, (int)pointIndex); 288 | } 289 | 290 | -(void)userClickedOnLinePoint:(CGPoint)point lineIndex:(NSInteger)lineIndex{ 291 | NSLog(@"Click on line %f, %f, line index is %d",point.x, point.y, (int)lineIndex); 292 | } 293 | 294 | ``` 295 | 296 | 297 | ## License 298 | 299 | This code is distributed under the terms and conditions of the [MIT license](LICENSE). 300 | 301 | ## SpecialThanks 302 | 303 | [@lexrus](http://twitter.com/lexrus) CocoaPods Spec 304 | [ZhangHang](http://zhanghang.github.com) Pie Chart 305 | [MrWooj](https://github.com/MrWooJ) Scatter Chart 306 | 307 | 308 | 309 | -------------------------------------------------------------------------------- /PNChart/PNRadarChart.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNRadarChart.m 3 | // PNChartDemo 4 | // 5 | // Created by Lei on 15/7/1. 6 | // Copyright (c) 2015年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNRadarChart.h" 10 | 11 | @interface PNRadarChart() 12 | 13 | @property (nonatomic) CGFloat centerX; 14 | @property (nonatomic) CGFloat centerY; 15 | @property (nonatomic) NSMutableArray *pointsToWebArrayArray; 16 | @property (nonatomic) NSMutableArray *pointsToPlotArray; 17 | @property (nonatomic) UILabel *detailLabel; 18 | @property (nonatomic) CGFloat lengthUnit; 19 | @property (nonatomic) CAShapeLayer *chartPlot; 20 | 21 | @end 22 | 23 | 24 | @implementation PNRadarChart 25 | 26 | - (id)initWithFrame:(CGRect)frame items:(NSArray *)items valueDivider:(CGFloat)unitValue { 27 | self=[super initWithFrame:frame]; 28 | if (self) { 29 | self.backgroundColor = [UIColor clearColor]; 30 | self.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 31 | 32 | //Public iVar 33 | 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 34 | { 35 | NSLog( @"At least three items!"); 36 | NSArray *defaultArray = @[[PNRadarChartDataItem dataItemWithValue:0 description:@"Default"], 37 | [PNRadarChartDataItem dataItemWithValue:0 description:@"Default"], 38 | [PNRadarChartDataItem dataItemWithValue:0 description:@"Default"], 39 | ]; 40 | defaultArray = [defaultArray arrayByAddingObjectsFromArray:items]; 41 | _chartData = [NSArray arrayWithArray:defaultArray]; 42 | }else{ 43 | _chartData = [NSArray arrayWithArray:items]; 44 | } 45 | _valueDivider = unitValue; 46 | _maxValue = 1; 47 | _webColor = [UIColor grayColor]; 48 | _plotColor = [UIColor colorWithRed:.4 green:.8 blue:.4 alpha:.7]; 49 | _fontColor = [UIColor blackColor]; 50 | _graduationColor = [UIColor orangeColor]; 51 | _fontSize = 15; 52 | _labelStyle = PNRadarChartLabelStyleHorizontal; 53 | _isLabelTouchable = YES; 54 | _isShowGraduation = NO; 55 | 56 | //Private iVar 57 | _centerX = frame.size.width/2; 58 | _centerY = frame.size.height/2; 59 | _pointsToWebArrayArray = [NSMutableArray array]; 60 | _pointsToPlotArray = [NSMutableArray array]; 61 | _lengthUnit = 0; 62 | _chartPlot = [CAShapeLayer layer]; 63 | _chartPlot.lineCap = kCALineCapButt; 64 | _chartPlot.lineWidth = 1.0; 65 | [self.layer addSublayer:_chartPlot]; 66 | 67 | [super setupDefaultValues]; 68 | //init detailLabel 69 | _detailLabel = [[UILabel alloc] init]; 70 | _detailLabel.backgroundColor = [UIColor colorWithRed:.9 green:.9 blue:.1 alpha:.9]; 71 | _detailLabel.textAlignment = NSTextAlignmentCenter; 72 | _detailLabel.textColor = [UIColor colorWithWhite:1 alpha:1]; 73 | _detailLabel.font = [UIFont systemFontOfSize:15]; 74 | [_detailLabel setHidden:YES]; 75 | [self addSubview:_detailLabel]; 76 | 77 | [self strokeChart]; 78 | } 79 | return self; 80 | } 81 | 82 | #pragma mark - main 83 | - (void)calculateChartPoints { 84 | [_pointsToPlotArray removeAllObjects]; 85 | [_pointsToWebArrayArray removeAllObjects]; 86 | 87 | //init Descriptions , Values and Angles. 88 | NSMutableArray *descriptions = [NSMutableArray array]; 89 | NSMutableArray *values = [NSMutableArray array]; 90 | NSMutableArray *angles = [NSMutableArray array]; 91 | for (int i=0;i<_chartData.count;i++) { 92 | PNRadarChartDataItem *item = (PNRadarChartDataItem *)[_chartData objectAtIndex:i]; 93 | [descriptions addObject:item.textDescription]; 94 | [values addObject:[NSNumber numberWithFloat:item.value]]; 95 | CGFloat angleValue = (float)i/(float)[_chartData count]*2*M_PI + M_PI_2 * 3; 96 | [angles addObject:[NSNumber numberWithFloat:angleValue]]; 97 | } 98 | 99 | //calculate all the lengths 100 | _maxValue = [self getMaxValueFromArray:values]; 101 | CGFloat margin = 0; 102 | if (_labelStyle==PNRadarChartLabelStyleCircle) { 103 | margin = MIN(_centerX , _centerY)*3/10; 104 | }else if (_labelStyle==PNRadarChartLabelStyleHorizontal) { 105 | margin = [self getMaxWidthLabelFromArray:descriptions withFontSize:_fontSize]; 106 | } 107 | CGFloat maxLength = ceil(MIN(_centerX, _centerY) - margin); 108 | int plotCircles = (_maxValue/_valueDivider); 109 | if (plotCircles > MAXCIRCLE) { 110 | NSLog(@"Circle number is higher than max"); 111 | plotCircles = MAXCIRCLE; 112 | _valueDivider = _maxValue/plotCircles; 113 | } 114 | _lengthUnit = maxLength/plotCircles; 115 | NSArray *lengthArray = [self getLengthArrayWithCircleNum:(int)plotCircles]; 116 | 117 | //get all the points and plot 118 | for (NSNumber *lengthNumber in lengthArray) { 119 | CGFloat length = [lengthNumber floatValue]; 120 | [_pointsToWebArrayArray addObject:[self getWebPointWithLength:length angleArray:angles]]; 121 | } 122 | int section = 0; 123 | for (id value in values) { 124 | CGFloat valueFloat = [value floatValue]; 125 | if (valueFloat>_maxValue) { 126 | NSString *reason = [NSString stringWithFormat:@"Value number is higher than max -value: %f - maxValue: %f",valueFloat,_maxValue]; 127 | @throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil]; 128 | return; 129 | } 130 | 131 | CGFloat length = valueFloat/_maxValue*maxLength; 132 | CGFloat angle = [[angles objectAtIndex:section] floatValue]; 133 | CGFloat x = _centerX +length*cos(angle); 134 | CGFloat y = _centerY +length*sin(angle); 135 | NSValue* point = [NSValue valueWithCGPoint:CGPointMake(x, y)]; 136 | [_pointsToPlotArray addObject:point]; 137 | section++; 138 | } 139 | //set the labels 140 | [self drawLabelWithMaxLength:maxLength labelArray:descriptions angleArray:angles]; 141 | 142 | } 143 | #pragma mark - Draw 144 | 145 | - (void)drawRect:(CGRect)rect { 146 | // Drawing backgound 147 | CGContextRef context = UIGraphicsGetCurrentContext(); 148 | CGContextClearRect(context, rect); 149 | int section = 0; 150 | //circles 151 | for(NSArray *pointArray in _pointsToWebArrayArray){ 152 | //plot backgound 153 | CGContextRef graphContext = UIGraphicsGetCurrentContext(); 154 | CGContextBeginPath(graphContext); 155 | CGPoint beginPoint = [[pointArray objectAtIndex:0] CGPointValue]; 156 | CGContextMoveToPoint(graphContext, beginPoint.x, beginPoint.y); 157 | for(NSValue* pointValue in pointArray){ 158 | CGPoint point = [pointValue CGPointValue]; 159 | CGContextAddLineToPoint(graphContext, point.x, point.y); 160 | } 161 | CGContextAddLineToPoint(graphContext, beginPoint.x, beginPoint.y); 162 | CGContextSetStrokeColorWithColor(graphContext, _webColor.CGColor); 163 | CGContextStrokePath(graphContext); 164 | 165 | } 166 | //cuts 167 | NSArray *largestPointArray = [_pointsToWebArrayArray lastObject]; 168 | for (NSValue *pointValue in largestPointArray){ 169 | section++; 170 | if (section==1&&_isShowGraduation)continue; 171 | 172 | CGContextRef graphContext = UIGraphicsGetCurrentContext(); 173 | CGContextBeginPath(graphContext); 174 | CGContextMoveToPoint(graphContext, _centerX, _centerY); 175 | CGPoint point = [pointValue CGPointValue]; 176 | CGContextAddLineToPoint(graphContext, point.x, point.y); 177 | CGContextSetStrokeColorWithColor(graphContext, _webColor.CGColor); 178 | CGContextStrokePath(graphContext); 179 | } 180 | 181 | 182 | } 183 | 184 | - (void)strokeChart { 185 | 186 | [self calculateChartPoints]; 187 | [self setNeedsDisplay]; 188 | [_detailLabel setHidden:YES]; 189 | 190 | //Draw plot 191 | [_chartPlot removeAllAnimations]; 192 | UIBezierPath *plotline = [UIBezierPath bezierPath]; 193 | CGPoint beginPoint = [[_pointsToPlotArray objectAtIndex:0] CGPointValue]; 194 | [plotline moveToPoint:CGPointMake(beginPoint.x, beginPoint.y)]; 195 | for(NSValue *pointValue in _pointsToPlotArray){ 196 | CGPoint point = [pointValue CGPointValue]; 197 | [plotline addLineToPoint:CGPointMake(point.x ,point.y)]; 198 | 199 | } 200 | [plotline setLineWidth:1]; 201 | [plotline setLineCapStyle:kCGLineCapButt]; 202 | 203 | _chartPlot.path = plotline.CGPath; 204 | 205 | _chartPlot.fillColor = _plotColor.CGColor; 206 | 207 | [self addAnimationIfNeeded]; 208 | [self showGraduation]; 209 | 210 | // self.transform = CGAffineTransformMakeRotation(-M_PI_2); 211 | } 212 | 213 | #pragma mark - Helper 214 | 215 | - (void)drawLabelWithMaxLength:(CGFloat)maxLength labelArray:(NSArray *)labelArray angleArray:(NSArray *)angleArray { 216 | //set labels 217 | int labelTag = 121; 218 | while (true) { 219 | UIView *label = [self viewWithTag:labelTag]; 220 | if(!label)break; 221 | [label removeFromSuperview]; 222 | } 223 | int section = 0; 224 | CGFloat labelLength = maxLength + maxLength/10; 225 | 226 | for (NSString *labelString in labelArray) { 227 | CGFloat angle = [[angleArray objectAtIndex:section] floatValue]; 228 | CGFloat x = _centerX + labelLength *cos(angle); 229 | CGFloat y = _centerY + labelLength *sin(angle); 230 | 231 | UILabel *label = [[UILabel alloc] init] ; 232 | label.backgroundColor = [UIColor clearColor]; 233 | label.font = [UIFont systemFontOfSize:_fontSize]; 234 | label.textColor = _fontColor; 235 | label.text = labelString; 236 | label.tag = labelTag; 237 | CGSize detailSize = [labelString sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:_fontSize]}]; 238 | 239 | switch (_labelStyle) { 240 | case PNRadarChartLabelStyleCircle: 241 | label.frame = CGRectMake(x-5*_fontSize/2, y-_fontSize/2, 5*_fontSize, _fontSize); 242 | label.transform = CGAffineTransformMakeRotation(((float)section/[labelArray count])*(2*M_PI)+M_PI_2 + M_PI_2 * 3); 243 | label.textAlignment = NSTextAlignmentCenter; 244 | break; 245 | case PNRadarChartLabelStyleHorizontal: 246 | if (x<_centerX) { 247 | label.frame = CGRectMake(x-detailSize.width, y-detailSize.height/2, detailSize.width, detailSize.height); 248 | label.textAlignment = NSTextAlignmentRight; 249 | }else{ 250 | label.frame = CGRectMake(x, y-detailSize.height/2, detailSize.width , detailSize.height); 251 | label.textAlignment = NSTextAlignmentLeft; 252 | } 253 | if ((int)x == (int)_centerX) { 254 | label.frame = CGRectMake(x - detailSize.width * 0.5, y - detailSize.height * 0.5, detailSize.width , detailSize.height); 255 | label.textAlignment = NSTextAlignmentCenter; 256 | } 257 | break; 258 | case PNRadarChartLabelStyleHidden: 259 | [label setHidden:YES]; 260 | break; 261 | default: 262 | break; 263 | } 264 | [label sizeToFit]; 265 | 266 | if (_isLabelTouchable) { 267 | label.userInteractionEnabled = YES; 268 | UITapGestureRecognizer *tapLabelGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapLabel:)]; 269 | [label addGestureRecognizer:tapLabelGesture]; 270 | } 271 | [self addSubview:label]; 272 | 273 | section ++; 274 | } 275 | 276 | } 277 | 278 | - (void)tapLabel:(UITapGestureRecognizer *)recognizer { 279 | UILabel *label=(UILabel*)recognizer.view; 280 | _detailLabel.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y-30, 50, 25); 281 | for (PNRadarChartDataItem *item in _chartData) { 282 | if ([label.text isEqualToString:item.textDescription]) { 283 | _detailLabel.text = [NSString stringWithFormat:@"%.2f", item.value]; 284 | break; 285 | } 286 | } 287 | [_detailLabel setHidden:NO]; 288 | } 289 | 290 | - (void)showGraduation { 291 | int labelTag = 112; 292 | while (true) { 293 | UIView *label = [self viewWithTag:labelTag]; 294 | if(!label)break; 295 | [label removeFromSuperview]; 296 | } 297 | int section = 0; 298 | for (NSArray *pointsArray in _pointsToWebArrayArray) { 299 | section++; 300 | CGPoint labelPoint = [[pointsArray objectAtIndex:0] CGPointValue]; 301 | UILabel *graduationLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelPoint.x-_lengthUnit, labelPoint.y-_lengthUnit*5/8, _lengthUnit*5/8, _lengthUnit)]; 302 | graduationLabel.adjustsFontSizeToFitWidth = YES; 303 | graduationLabel.tag = labelTag; 304 | graduationLabel.font = [UIFont systemFontOfSize:ceil(_lengthUnit)]; 305 | graduationLabel.textColor = _graduationColor; 306 | graduationLabel.text = [NSString stringWithFormat:@"%.0f",_valueDivider*section]; 307 | [self addSubview:graduationLabel]; 308 | if (_isShowGraduation) { 309 | [graduationLabel setHidden:NO]; 310 | }else{ 311 | [graduationLabel setHidden:YES];} 312 | } 313 | 314 | } 315 | 316 | - (NSArray *)getWebPointWithLength:(CGFloat)length angleArray:(NSArray *)angleArray { 317 | NSMutableArray *pointArray = [NSMutableArray array]; 318 | for (NSNumber *angleNumber in angleArray) { 319 | CGFloat angle = [angleNumber floatValue]; 320 | CGFloat x = _centerX + length*cos(angle); 321 | CGFloat y = _centerY + length*sin(angle); 322 | [pointArray addObject:[NSValue valueWithCGPoint:CGPointMake(x,y)]]; 323 | } 324 | return pointArray; 325 | 326 | } 327 | 328 | - (NSArray *)getLengthArrayWithCircleNum:(int)plotCircles { 329 | NSMutableArray *lengthArray = [NSMutableArray array]; 330 | CGFloat length = 0; 331 | for (int i = 0; i < plotCircles; i++) { 332 | length += _lengthUnit; 333 | [lengthArray addObject:[NSNumber numberWithFloat:length]]; 334 | } 335 | return lengthArray; 336 | } 337 | 338 | - (CGFloat)getMaxWidthLabelFromArray:(NSArray *)keyArray withFontSize:(CGFloat)size { 339 | CGFloat maxWidth = 0; 340 | for (NSString *str in keyArray) { 341 | CGSize detailSize = [str sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:_fontSize]}]; 342 | maxWidth = MAX(maxWidth, detailSize.width); 343 | } 344 | return maxWidth; 345 | } 346 | 347 | - (CGFloat)getMaxValueFromArray:(NSArray *)valueArray { 348 | CGFloat max = _maxValue; 349 | for (NSNumber *valueNum in valueArray) { 350 | CGFloat valueFloat = [valueNum floatValue]; 351 | max = MAX(valueFloat, max); 352 | } 353 | return ceil(max); 354 | } 355 | 356 | - (void)addAnimationIfNeeded 357 | { 358 | if (self.displayAnimated) { 359 | CABasicAnimation *animateScale = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; 360 | animateScale.fromValue = [NSNumber numberWithFloat:0.f]; 361 | animateScale.toValue = [NSNumber numberWithFloat:1.0f]; 362 | 363 | CABasicAnimation *animateMove = [CABasicAnimation animationWithKeyPath:@"position"]; 364 | animateMove.fromValue = [NSValue valueWithCGPoint:CGPointMake(_centerX, _centerY)]; 365 | animateMove.toValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)]; 366 | 367 | CABasicAnimation *animateAlpha = [CABasicAnimation animationWithKeyPath:@"opacity"]; 368 | animateAlpha.fromValue = [NSNumber numberWithFloat:0.f]; 369 | 370 | CAAnimationGroup *aniGroup = [CAAnimationGroup animation]; 371 | aniGroup.duration = 1.f; 372 | aniGroup.repeatCount = 1; 373 | aniGroup.animations = [NSArray arrayWithObjects:animateScale,animateMove,animateAlpha, nil]; 374 | aniGroup.removedOnCompletion = YES; 375 | 376 | [_chartPlot addAnimation:aniGroup forKey:nil]; 377 | } 378 | } 379 | 380 | @end 381 | -------------------------------------------------------------------------------- /PNChart/PNBarChart.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNBarChart.m 3 | // PNChartDemo 4 | // 5 | // Created by kevin on 11/7/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNBarChart.h" 10 | #import "PNColor.h" 11 | #import "PNChartLabel.h" 12 | 13 | @interface PNBarChart () { 14 | NSMutableArray *_xChartLabels; 15 | NSMutableArray *_yChartLabels; 16 | } 17 | 18 | - (UIColor *)barColorAtIndex:(NSUInteger)index; 19 | 20 | @end 21 | 22 | @implementation PNBarChart 23 | 24 | - (id)initWithCoder:(NSCoder *)aDecoder 25 | { 26 | self = [super initWithCoder:aDecoder]; 27 | 28 | if (self) { 29 | [self setupDefaultValues]; 30 | } 31 | return self; 32 | } 33 | 34 | - (id)initWithFrame:(CGRect)frame 35 | { 36 | self = [super initWithFrame:frame]; 37 | 38 | if (self) { 39 | [self setupDefaultValues]; 40 | } 41 | 42 | return self; 43 | } 44 | 45 | - (void)setupDefaultValues 46 | { 47 | [super setupDefaultValues]; 48 | self.backgroundColor = [UIColor whiteColor]; 49 | self.clipsToBounds = YES; 50 | _showLabel = YES; 51 | _barBackgroundColor = PNLightGrey; 52 | _labelTextColor = [UIColor grayColor]; 53 | _labelFont = [UIFont systemFontOfSize:11.0f]; 54 | _xChartLabels = [NSMutableArray array]; 55 | _yChartLabels = [NSMutableArray array]; 56 | _bars = [NSMutableArray array]; 57 | _xLabelSkip = 1; 58 | _yLabelSum = 4; 59 | _labelMarginTop = 2; 60 | _chartMarginLeft = 25.0; 61 | _chartMarginRight = 25.0; 62 | _chartMarginTop = 25.0; 63 | _chartMarginBottom = 25.0; 64 | _barRadius = 2.0; 65 | _showChartBorder = NO; 66 | _chartBorderColor = PNLightGrey; 67 | _showLevelLine = NO; 68 | _yChartLabelWidth = 18; 69 | _rotateForXAxisText = false; 70 | _isGradientShow = YES; 71 | _isShowNumbers = YES; 72 | _yLabelPrefix = @""; 73 | _yLabelSuffix = @""; 74 | _yLabelFormatter = ^(CGFloat yValue){ 75 | return [NSString stringWithFormat:@"%1.f",yValue]; 76 | }; 77 | } 78 | 79 | - (void)setYValues:(NSArray *)yValues 80 | { 81 | _yValues = yValues; 82 | //make the _yLabelSum value dependant of the distinct values of yValues to avoid duplicates on yAxis 83 | 84 | if (_showLabel) { 85 | [self __addYCoordinateLabelsValues]; 86 | } else { 87 | [self processYMaxValue]; 88 | } 89 | } 90 | 91 | - (void)processYMaxValue { 92 | NSArray *yAxisValues = _yLabels ? _yLabels : _yValues; 93 | _yLabelSum = _yLabels ? _yLabels.count - 1 :_yLabelSum; 94 | if (_yMaxValue) { 95 | _yValueMax = _yMaxValue; 96 | } else { 97 | [self getYValueMax:yAxisValues]; 98 | } 99 | 100 | if (_yLabelSum==4) { 101 | _yLabelSum = yAxisValues.count; 102 | (_yLabelSum % 2 == 0) ? _yLabelSum : _yLabelSum++; 103 | } 104 | } 105 | 106 | #pragma mark - Private Method 107 | #pragma mark - Add Y Label 108 | - (void)__addYCoordinateLabelsValues{ 109 | 110 | [self viewCleanupForCollection:_yChartLabels]; 111 | 112 | [self processYMaxValue]; 113 | 114 | float sectionHeight = (self.frame.size.height - _chartMarginTop - _chartMarginBottom - kXLabelHeight) / _yLabelSum; 115 | 116 | for (int i = 0; i <= _yLabelSum; i++) { 117 | NSString *labelText; 118 | if (_yLabels) { 119 | float yAsixValue = [_yLabels[_yLabels.count - i - 1] floatValue]; 120 | labelText= _yLabelFormatter(yAsixValue); 121 | } else { 122 | labelText = _yLabelFormatter((float)_yValueMax * ( (_yLabelSum - i) / (float)_yLabelSum )); 123 | } 124 | 125 | PNChartLabel *label = [[PNChartLabel alloc] initWithFrame:CGRectZero]; 126 | label.font = _labelFont; 127 | label.textColor = _labelTextColor; 128 | [label setTextAlignment:NSTextAlignmentRight]; 129 | label.text = [NSString stringWithFormat:@"%@%@%@", _yLabelPrefix, labelText, _yLabelSuffix]; 130 | 131 | [self addSubview:label]; 132 | 133 | label.frame = (CGRect){0, sectionHeight * i + _chartMarginTop - kYLabelHeight/2.0 + kXLabelHeight + _labelMarginTop, _yChartLabelWidth, kYLabelHeight}; 134 | 135 | [_yChartLabels addObject:label]; 136 | } 137 | } 138 | 139 | -(void)updateChartData:(NSArray *)data{ 140 | self.yValues = data; 141 | [self updateBar]; 142 | } 143 | 144 | - (void)getYValueMax:(NSArray *)yLabels 145 | { 146 | CGFloat max = [[yLabels valueForKeyPath:@"@max.floatValue"] floatValue]; 147 | 148 | //ensure max is even 149 | _yValueMax = max ; 150 | 151 | if (_yValueMax == 0) { 152 | _yValueMax = _yMinValue; 153 | } 154 | } 155 | 156 | - (void)setXLabels:(NSArray *)xLabels 157 | { 158 | _xLabels = xLabels; 159 | 160 | if (_xChartLabels) { 161 | [self viewCleanupForCollection:_xChartLabels]; 162 | }else{ 163 | _xChartLabels = [NSMutableArray new]; 164 | } 165 | 166 | _xLabelWidth = (self.frame.size.width - _chartMarginLeft - _chartMarginRight) / [xLabels count]; 167 | 168 | if (_showLabel) { 169 | int labelAddCount = 0; 170 | for (int index = 0; index < _xLabels.count; index++) { 171 | labelAddCount += 1; 172 | 173 | if (labelAddCount == _xLabelSkip) { 174 | NSString *labelText = [_xLabels[index] description]; 175 | PNChartLabel * label = [[PNChartLabel alloc] initWithFrame:CGRectMake(0, 0, _xLabelWidth, kXLabelHeight)]; 176 | label.font = _labelFont; 177 | label.textColor = _labelTextColor; 178 | [label setTextAlignment:NSTextAlignmentCenter]; 179 | label.text = labelText; 180 | //[label sizeToFit]; 181 | CGFloat labelXPosition; 182 | if (_rotateForXAxisText){ 183 | label.transform = CGAffineTransformMakeRotation(M_PI / 4); 184 | labelXPosition = (index * _xLabelWidth + _chartMarginLeft + _xLabelWidth /1.5); 185 | } 186 | else{ 187 | labelXPosition = (index * _xLabelWidth + _chartMarginLeft + _xLabelWidth /2.0 ); 188 | } 189 | label.center = CGPointMake(labelXPosition, 190 | self.frame.size.height - _chartMarginTop + label.frame.size.height /2.0 + _labelMarginTop); 191 | labelAddCount = 0; 192 | 193 | [_xChartLabels addObject:label]; 194 | [self addSubview:label]; 195 | } 196 | } 197 | } 198 | } 199 | 200 | 201 | - (void)setStrokeColor:(UIColor *)strokeColor 202 | { 203 | _strokeColor = strokeColor; 204 | } 205 | 206 | - (void)updateBar 207 | { 208 | 209 | //Add bars 210 | CGFloat chartCavanHeight = self.frame.size.height - _chartMarginTop - _chartMarginBottom - kXLabelHeight; 211 | NSInteger index = 0; 212 | 213 | for (NSNumber *valueString in _yValues) { 214 | 215 | PNBar *bar; 216 | 217 | if (_bars.count == _yValues.count) { 218 | bar = [_bars objectAtIndex:index]; 219 | }else{ 220 | CGFloat barWidth; 221 | CGFloat barXPosition; 222 | 223 | if (_barWidth) { 224 | barWidth = _barWidth; 225 | barXPosition = index * _xLabelWidth + _chartMarginLeft + _xLabelWidth /2.0 - _barWidth /2.0; 226 | }else{ 227 | barXPosition = index * _xLabelWidth + _chartMarginLeft + _xLabelWidth * 0.25; 228 | if (_showLabel) { 229 | barWidth = _xLabelWidth * 0.5; 230 | 231 | } 232 | else { 233 | barWidth = _xLabelWidth * 0.6; 234 | 235 | } 236 | } 237 | 238 | bar = [[PNBar alloc] initWithFrame:CGRectMake(barXPosition, //Bar X position 239 | self.frame.size.height - chartCavanHeight - kXLabelHeight - _chartMarginBottom + _chartMarginTop , //Bar Y position 240 | barWidth, // Bar witdh 241 | self.showLevelLine ? chartCavanHeight/2.0:chartCavanHeight)]; //Bar height 242 | 243 | //Change Bar Radius 244 | bar.barRadius = _barRadius; 245 | 246 | //Set Bar Animation 247 | bar.displayAnimated = self.displayAnimated; 248 | 249 | //Change Bar Background color 250 | bar.backgroundColor = _barBackgroundColor; 251 | //Bar StrokColor First 252 | if (self.strokeColor) { 253 | bar.barColor = self.strokeColor; 254 | }else{ 255 | bar.barColor = [self barColorAtIndex:index]; 256 | } 257 | 258 | if (self.labelTextColor) { 259 | bar.labelTextColor = self.labelTextColor; 260 | } 261 | 262 | // Add gradient 263 | if (self.isGradientShow) { 264 | bar.barColorGradientStart = bar.barColor; 265 | } 266 | 267 | //For Click Index 268 | bar.tag = index; 269 | 270 | [_bars addObject:bar]; 271 | [self addSubview:bar]; 272 | } 273 | 274 | //Height Of Bar 275 | float value = [valueString floatValue]; 276 | float grade =fabsf((float)value / (float)_yValueMax); 277 | 278 | if (isnan(grade)) { 279 | grade = 0; 280 | } 281 | bar.maxDivisor = (float)_yValueMax; 282 | bar.grade = grade; 283 | bar.isShowNumber = self.isShowNumbers; 284 | CGRect originalFrame = bar.frame; 285 | NSString *currentNumber = [NSString stringWithFormat:@"%f",value]; 286 | 287 | if ([[currentNumber substringToIndex:1] isEqualToString:@"-"] && self.showLevelLine) { 288 | CGAffineTransform transform =CGAffineTransformMakeRotation(M_PI); 289 | [bar setTransform:transform]; 290 | originalFrame.origin.y = bar.frame.origin.y + bar.frame.size.height; 291 | bar.frame = originalFrame; 292 | bar.isNegative = YES; 293 | 294 | } 295 | index += 1; 296 | } 297 | } 298 | 299 | - (void)strokeChart 300 | { 301 | //Add Labels 302 | 303 | [self viewCleanupForCollection:_bars]; 304 | 305 | 306 | //Update Bar 307 | 308 | [self updateBar]; 309 | 310 | //Add chart border lines 311 | 312 | if (_showChartBorder) { 313 | _chartBottomLine = [CAShapeLayer layer]; 314 | _chartBottomLine.lineCap = kCALineCapButt; 315 | _chartBottomLine.fillColor = [[UIColor whiteColor] CGColor]; 316 | _chartBottomLine.lineWidth = 1.0; 317 | _chartBottomLine.strokeEnd = 0.0; 318 | 319 | UIBezierPath *progressline = [UIBezierPath bezierPath]; 320 | 321 | [progressline moveToPoint:CGPointMake(_chartMarginLeft, self.frame.size.height - kXLabelHeight - _chartMarginBottom + _chartMarginTop)]; 322 | [progressline addLineToPoint:CGPointMake(self.frame.size.width - _chartMarginRight, self.frame.size.height - kXLabelHeight - _chartMarginBottom + _chartMarginTop)]; 323 | 324 | [progressline setLineWidth:1.0]; 325 | [progressline setLineCapStyle:kCGLineCapSquare]; 326 | _chartBottomLine.path = progressline.CGPath; 327 | _chartBottomLine.strokeColor = [_chartBorderColor CGColor];; 328 | _chartBottomLine.strokeEnd = 1.0; 329 | 330 | [self.layer addSublayer:_chartBottomLine]; 331 | 332 | //Add left Chart Line 333 | 334 | _chartLeftLine = [CAShapeLayer layer]; 335 | _chartLeftLine.lineCap = kCALineCapButt; 336 | _chartLeftLine.fillColor = [[UIColor whiteColor] CGColor]; 337 | _chartLeftLine.lineWidth = 1.0; 338 | _chartLeftLine.strokeEnd = 0.0; 339 | 340 | UIBezierPath *progressLeftline = [UIBezierPath bezierPath]; 341 | 342 | [progressLeftline moveToPoint:CGPointMake(_chartMarginLeft, self.frame.size.height - kXLabelHeight - _chartMarginBottom + _chartMarginTop)]; 343 | [progressLeftline addLineToPoint:CGPointMake(_chartMarginLeft, _chartMarginTop)]; 344 | 345 | [progressLeftline setLineWidth:1.0]; 346 | [progressLeftline setLineCapStyle:kCGLineCapSquare]; 347 | _chartLeftLine.path = progressLeftline.CGPath; 348 | _chartLeftLine.strokeColor = [_chartBorderColor CGColor]; 349 | _chartLeftLine.strokeEnd = 1.0; 350 | 351 | [self addBorderAnimationIfNeeded]; 352 | [self.layer addSublayer:_chartLeftLine]; 353 | } 354 | 355 | // Add Level Separator Line 356 | if (_showLevelLine) { 357 | _chartLevelLine = [CAShapeLayer layer]; 358 | _chartLevelLine.lineCap = kCALineCapButt; 359 | _chartLevelLine.fillColor = [[UIColor whiteColor] CGColor]; 360 | _chartLevelLine.lineWidth = 1.0; 361 | _chartLevelLine.strokeEnd = 0.0; 362 | 363 | UIBezierPath *progressline = [UIBezierPath bezierPath]; 364 | 365 | [progressline moveToPoint:CGPointMake(_chartMarginLeft, (self.frame.size.height - kXLabelHeight )/2.0)]; 366 | [progressline addLineToPoint:CGPointMake(self.frame.size.width - _chartMarginLeft - _chartMarginRight, (self.frame.size.height - kXLabelHeight )/2.0)]; 367 | 368 | [progressline setLineWidth:1.0]; 369 | [progressline setLineCapStyle:kCGLineCapSquare]; 370 | _chartLevelLine.path = progressline.CGPath; 371 | 372 | _chartLevelLine.strokeColor = PNLightGrey.CGColor; 373 | 374 | [self addSeparatorAnimationIfNeeded]; 375 | _chartLevelLine.strokeEnd = 1.0; 376 | 377 | [self.layer addSublayer:_chartLevelLine]; 378 | } else { 379 | if (_chartLevelLine) { 380 | [_chartLevelLine removeFromSuperlayer]; 381 | _chartLevelLine = nil; 382 | } 383 | } 384 | } 385 | 386 | - (void)addBorderAnimationIfNeeded 387 | { 388 | if (self.displayAnimated) { 389 | CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 390 | pathAnimation.duration = 0.5; 391 | pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 392 | pathAnimation.fromValue = @0.0f; 393 | pathAnimation.toValue = @1.0f; 394 | [_chartBottomLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; 395 | 396 | CABasicAnimation *pathLeftAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 397 | pathLeftAnimation.duration = 0.5; 398 | pathLeftAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 399 | pathLeftAnimation.fromValue = @0.0f; 400 | pathLeftAnimation.toValue = @1.0f; 401 | [_chartLeftLine addAnimation:pathLeftAnimation forKey:@"strokeEndAnimation"]; 402 | } 403 | } 404 | 405 | - (void)addSeparatorAnimationIfNeeded 406 | { 407 | if (self.displayAnimated) { 408 | CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 409 | pathAnimation.duration = 0.5; 410 | pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 411 | pathAnimation.fromValue = @0.0f; 412 | pathAnimation.toValue = @1.0f; 413 | [_chartLevelLine addAnimation:pathAnimation forKey:@"strokeEndAnimation"]; 414 | } 415 | } 416 | 417 | - (void)viewCleanupForCollection:(NSMutableArray *)array 418 | { 419 | if (array.count) { 420 | [array makeObjectsPerformSelector:@selector(removeFromSuperview)]; 421 | [array removeAllObjects]; 422 | } 423 | } 424 | 425 | 426 | #pragma mark - Class extension methods 427 | 428 | - (UIColor *)barColorAtIndex:(NSUInteger)index 429 | { 430 | if ([self.strokeColors count] == [self.yValues count]) { 431 | return self.strokeColors[index]; 432 | } 433 | else { 434 | return self.strokeColor; 435 | } 436 | } 437 | 438 | #pragma mark - Touch detection 439 | 440 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 441 | { 442 | [self touchPoint:touches withEvent:event]; 443 | [super touchesBegan:touches withEvent:event]; 444 | } 445 | 446 | - (void)touchPoint:(NSSet *)touches withEvent:(UIEvent *)event 447 | { 448 | //Get the point user touched 449 | UITouch *touch = [touches anyObject]; 450 | CGPoint touchPoint = [touch locationInView:self]; 451 | UIView *subview = [self hitTest:touchPoint withEvent:nil]; 452 | 453 | if ([subview isKindOfClass:[PNBar class]] && [self.delegate respondsToSelector:@selector(userClickedOnBarAtIndex:)]) { 454 | [self.delegate userClickedOnBarAtIndex:subview.tag]; 455 | } 456 | } 457 | 458 | 459 | @end 460 | -------------------------------------------------------------------------------- /PNChart/PNScatterChart.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNScatterChart.m 3 | // PNChartDemo 4 | // 5 | // Created by Alireza Arabi on 12/4/14. 6 | // Copyright (c) 2014 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNScatterChart.h" 10 | #import "PNColor.h" 11 | #import "PNChartLabel.h" 12 | #import "PNScatterChartData.h" 13 | #import "PNScatterChartDataItem.h" 14 | 15 | @interface PNScatterChart () 16 | 17 | @property (nonatomic, weak) CAShapeLayer *pathLayer; 18 | @property (nonatomic, weak) NSMutableArray *verticalLineLayer; 19 | @property (nonatomic, weak) NSMutableArray *horizentalLinepathLayer; 20 | 21 | @property (nonatomic) CGPoint startPoint; 22 | 23 | @property (nonatomic) CGPoint startPointVectorX; 24 | @property (nonatomic) CGPoint endPointVecotrX; 25 | 26 | @property (nonatomic) CGPoint startPointVectorY; 27 | @property (nonatomic) CGPoint endPointVecotrY; 28 | 29 | @property (nonatomic) CGFloat vectorX_Steps; 30 | @property (nonatomic) CGFloat vectorY_Steps; 31 | 32 | @property (nonatomic) CGFloat vectorX_Size; 33 | @property (nonatomic) CGFloat vectorY_Size; 34 | 35 | @property (nonatomic) NSMutableArray *axisX_labels; 36 | @property (nonatomic) NSMutableArray *axisY_labels; 37 | 38 | @property (nonatomic) int AxisX_partNumber ; 39 | @property (nonatomic) int AxisY_partNumber ; 40 | 41 | @property (nonatomic) CGFloat AxisX_step ; 42 | @property (nonatomic) CGFloat AxisY_step ; 43 | 44 | @property (nonatomic) CGFloat AxisX_Margin; 45 | @property (nonatomic) CGFloat AxisY_Margin; 46 | 47 | @property (nonatomic) BOOL isForUpdate; 48 | 49 | @end 50 | 51 | 52 | @implementation PNScatterChart 53 | 54 | #pragma mark initialization 55 | 56 | - (id)initWithCoder:(NSCoder *)coder 57 | { 58 | self = [super initWithCoder:coder]; 59 | 60 | if (self) { 61 | [self setupDefaultValues]; 62 | } 63 | return self; 64 | } 65 | 66 | - (id)initWithFrame:(CGRect)frame 67 | { 68 | self = [super initWithFrame:frame]; 69 | 70 | if (self) { 71 | [self setupDefaultValues]; 72 | } 73 | return self; 74 | } 75 | 76 | - (void) setup 77 | { 78 | [self vectorXSetup]; 79 | [self vectorYSetup]; 80 | } 81 | 82 | - (void)setupDefaultValues 83 | { 84 | [super setupDefaultValues]; 85 | 86 | // Initialization code 87 | self.backgroundColor = [UIColor whiteColor]; 88 | self.clipsToBounds = YES; 89 | _showLabel = YES; 90 | _isForUpdate = NO; 91 | self.userInteractionEnabled = YES; 92 | 93 | // Coordinate Axis Default Values 94 | _showCoordinateAxis = YES; 95 | _axisColor = [UIColor colorWithRed:0.4f green:0.4f blue:0.4f alpha:1.f]; 96 | _axisWidth = 1.f; 97 | 98 | // Initialization code 99 | _AxisX_Margin = 30 ; 100 | _AxisY_Margin = 30 ; 101 | 102 | // self.frame = CGRectMake((SCREEN_WIDTH - self.frame.size.width) / 2, 200, self.frame.size.width, self.frame.size.height) ; 103 | self.backgroundColor = [UIColor clearColor]; 104 | 105 | _startPoint.y = self.frame.size.height - self.AxisY_Margin ; 106 | _startPoint.x = self.AxisX_Margin ; 107 | 108 | _axisX_labels = [NSMutableArray array]; 109 | _axisY_labels = [NSMutableArray array]; 110 | 111 | _descriptionTextColor = [UIColor blackColor]; 112 | _descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:9.0]; 113 | _descriptionTextShadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; 114 | _descriptionTextShadowOffset = CGSizeMake(0, 1); 115 | _duration = 1.0; 116 | 117 | } 118 | 119 | #pragma mark calculating axis 120 | 121 | - (void) setAxisXWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks 122 | { 123 | _AxisX_minValue = minVal ; 124 | _AxisX_maxValue = maxVal ; 125 | _AxisX_partNumber = numberOfTicks - 1; 126 | _AxisX_step = (float)((maxVal - minVal)/_AxisX_partNumber); 127 | 128 | NSString *LabelFormat = self.xLabelFormat ? : @"%1.f"; 129 | CGFloat tempValue = minVal ; 130 | UILabel *label = [[UILabel alloc] init]; 131 | label.text = [NSString stringWithFormat:LabelFormat,minVal] ; 132 | [_axisX_labels addObject:label]; 133 | for (int i = 0 ; i < _AxisX_partNumber; i++) { 134 | tempValue = tempValue + _AxisX_step; 135 | UILabel *tempLabel = [[UILabel alloc] init]; 136 | tempLabel.text = [NSString stringWithFormat:LabelFormat,tempValue] ; 137 | [_axisX_labels addObject:tempLabel]; 138 | } 139 | } 140 | 141 | - (void) setAxisYWithMinimumValue:(CGFloat)minVal andMaxValue:(CGFloat)maxVal toTicks:(int)numberOfTicks 142 | { 143 | _AxisY_minValue = minVal ; 144 | _AxisY_maxValue = maxVal ; 145 | _AxisY_partNumber = numberOfTicks - 1; 146 | _AxisY_step = (float)((maxVal - minVal)/_AxisY_partNumber); 147 | 148 | NSString *LabelFormat = self.yLabelFormat ? : @"%1.f"; 149 | CGFloat tempValue = minVal ; 150 | UILabel *label = [[UILabel alloc] init]; 151 | label.text = [NSString stringWithFormat:LabelFormat,minVal] ; 152 | [_axisY_labels addObject:label]; 153 | for (int i = 0 ; i < _AxisY_partNumber; i++) { 154 | tempValue = tempValue + _AxisY_step; 155 | UILabel *tempLabel = [[UILabel alloc] init]; 156 | tempLabel.text = [NSString stringWithFormat:LabelFormat,tempValue] ; 157 | [_axisY_labels addObject:tempLabel]; 158 | } 159 | } 160 | 161 | - (NSArray*) getAxisMinMax:(NSArray*)xValues 162 | { 163 | float min = [xValues[0] floatValue]; 164 | float max = [xValues[0] floatValue]; 165 | for (NSNumber *number in xValues) 166 | { 167 | if ([number floatValue] > max) 168 | max = [number floatValue]; 169 | 170 | if ([number floatValue] < min) 171 | min = [number floatValue]; 172 | } 173 | NSArray *result = @[[NSNumber numberWithFloat:min], [NSNumber numberWithFloat:max]]; 174 | 175 | 176 | return result; 177 | } 178 | 179 | - (void)setAxisXLabel:(NSArray *)array { 180 | if(array.count == ++_AxisX_partNumber){ 181 | [_axisX_labels removeAllObjects]; 182 | for(int i=0;i= _AxisX_minValue && xValue <= _AxisX_maxValue) || !(yValue >= _AxisY_minValue && yValue <= _AxisY_maxValue)) { 248 | NSLog(@"input is not in correct range."); 249 | exit(0); 250 | } 251 | xFinilizeValue = [self mappingIsForAxisX:true WithValue:xValue]; 252 | yFinilizeValue = [self mappingIsForAxisX:false WithValue:yValue]; 253 | CAShapeLayer *shape = [self drawingPointsForChartData:chartData AndWithX:xFinilizeValue AndWithY:yFinilizeValue]; 254 | self.pathLayer = shape ; 255 | [self.layer addSublayer:self.pathLayer]; 256 | 257 | [self addAnimationIfNeeded]; 258 | } 259 | } 260 | }); 261 | }); 262 | } 263 | 264 | - (void)addAnimationIfNeeded{ 265 | 266 | if (self.displayAnimated) { 267 | CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 268 | pathAnimation.duration = _duration; 269 | pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 270 | pathAnimation.fromValue = @(0.0f); 271 | pathAnimation.toValue = @(1.0f); 272 | pathAnimation.fillMode = kCAFillModeForwards; 273 | self.layer.opacity = 1; 274 | [self.pathLayer addAnimation:pathAnimation forKey:@"fade"]; 275 | } 276 | } 277 | 278 | - (CGFloat) mappingIsForAxisX : (BOOL) isForAxisX WithValue : (CGFloat) value{ 279 | 280 | if (isForAxisX) { 281 | float temp = _startPointVectorX.x + (_vectorX_Steps / 2) ; 282 | CGFloat xPos = temp + (((value - _AxisX_minValue)/_AxisX_step) * _vectorX_Steps) ; 283 | return xPos; 284 | } 285 | else { 286 | float temp = _startPointVectorY.y - (_vectorY_Steps / 2) ; 287 | CGFloat yPos = temp - (((value - _AxisY_minValue) /_AxisY_step) * _vectorY_Steps); 288 | return yPos; 289 | } 290 | return 0; 291 | } 292 | 293 | #pragma mark - Update Chart Data 294 | 295 | - (void)updateChartData:(NSArray *)data 296 | { 297 | _chartData = data; 298 | 299 | // will be work in future. 300 | } 301 | 302 | #pragma drawing methods 303 | 304 | - (void)drawRect:(CGRect)rect 305 | { 306 | [super drawRect:rect]; 307 | 308 | CGContextRef context = UIGraphicsGetCurrentContext(); 309 | if (_showCoordinateAxis) { 310 | CGContextSetStrokeColorWithColor(context, [_axisColor CGColor]); 311 | CGContextSetLineWidth(context, _axisWidth); 312 | //drawing x vector 313 | CGContextMoveToPoint(context, _startPoint.x, _startPoint.y); 314 | CGContextAddLineToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); 315 | //drawing y vector 316 | CGContextMoveToPoint(context, _startPoint.x, _startPoint.y); 317 | CGContextAddLineToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); 318 | //drawing x arrow vector 319 | CGContextMoveToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); 320 | CGContextAddLineToPoint(context, _endPointVecotrX.x - 5, _endPointVecotrX.y + 3); 321 | CGContextMoveToPoint(context, _endPointVecotrX.x, _endPointVecotrX.y); 322 | CGContextAddLineToPoint(context, _endPointVecotrX.x - 5, _endPointVecotrX.y - 3); 323 | //drawing y arrow vector 324 | CGContextMoveToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); 325 | CGContextAddLineToPoint(context, _endPointVecotrY.x - 3, _endPointVecotrY.y + 5); 326 | CGContextMoveToPoint(context, _endPointVecotrY.x, _endPointVecotrY.y); 327 | CGContextAddLineToPoint(context, _endPointVecotrY.x + 3, _endPointVecotrY.y + 5); 328 | } 329 | 330 | if (_showLabel) { 331 | //drawing x steps vector and putting axis x labels 332 | float temp = _startPointVectorX.x + (_vectorX_Steps / 2) ; 333 | for (int i = 0; i < _axisX_labels.count; i++) { 334 | UIBezierPath *path = [UIBezierPath bezierPath]; 335 | [path moveToPoint:CGPointMake(temp, _startPointVectorX.y - 2)]; 336 | [path addLineToPoint:CGPointMake(temp, _startPointVectorX.y + 3)]; 337 | CAShapeLayer *shapeLayer = [CAShapeLayer layer]; 338 | shapeLayer.path = [path CGPath]; 339 | shapeLayer.strokeColor = [_axisColor CGColor]; 340 | shapeLayer.lineWidth = _axisWidth; 341 | shapeLayer.fillColor = [_axisColor CGColor]; 342 | [self.horizentalLinepathLayer addObject:shapeLayer]; 343 | [self.layer addSublayer:shapeLayer]; 344 | UILabel *lb = [_axisX_labels objectAtIndex:i] ; 345 | [self showXLabel:lb InPosition:CGPointMake(temp - 15, _startPointVectorX.y + 10 )]; 346 | temp = temp + _vectorX_Steps ; 347 | } 348 | //drawing y steps vector and putting axis x labels 349 | temp = _startPointVectorY.y - (_vectorY_Steps / 2) ; 350 | for (int i = 0; i < _axisY_labels.count; i++) { 351 | UIBezierPath *path = [UIBezierPath bezierPath]; 352 | [path moveToPoint:CGPointMake(_startPointVectorY.x - 3, temp)]; 353 | [path addLineToPoint:CGPointMake( _startPointVectorY.x + 2, temp)]; 354 | CAShapeLayer *shapeLayer = [CAShapeLayer layer]; 355 | shapeLayer.path = [path CGPath]; 356 | shapeLayer.strokeColor = [_axisColor CGColor]; 357 | shapeLayer.lineWidth = _axisWidth; 358 | shapeLayer.fillColor = [_axisColor CGColor]; 359 | [self.verticalLineLayer addObject:shapeLayer]; 360 | [self.layer addSublayer:shapeLayer]; 361 | UILabel *lb = [_axisY_labels objectAtIndex:i]; 362 | [self showXLabel:lb InPosition:CGPointMake(_startPointVectorY.x - 30, temp - 5)]; 363 | temp = temp - _vectorY_Steps ; 364 | } 365 | } 366 | CGContextDrawPath(context, kCGPathStroke); 367 | } 368 | 369 | - (CAShapeLayer*) drawingPointsForChartData : (PNScatterChartData *) chartData AndWithX : (CGFloat) X AndWithY : (CGFloat) Y 370 | { 371 | if (chartData.inflexionPointStyle == PNScatterChartPointStyleCircle) { 372 | float radius = chartData.size; 373 | CAShapeLayer *circle = [CAShapeLayer layer]; 374 | // Make a circular shape 375 | circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(X - radius, Y - radius, 2.0*radius, 2.0*radius) 376 | cornerRadius:radius].CGPath; 377 | // Configure the appearence of the circle 378 | circle.fillColor = [chartData.fillColor CGColor]; 379 | circle.strokeColor = [chartData.strokeColor CGColor]; 380 | circle.lineWidth = 1; 381 | 382 | // Add to parent layer 383 | return circle; 384 | } 385 | else if (chartData.inflexionPointStyle == PNScatterChartPointStyleSquare) { 386 | float side = chartData.size; 387 | CAShapeLayer *square = [CAShapeLayer layer]; 388 | // Make a circular shape 389 | square.path = [UIBezierPath bezierPathWithRect:CGRectMake(X - (side/2) , Y - (side/2), side, side)].CGPath ; 390 | // Configure the apperence of the circle 391 | square.fillColor = [chartData.fillColor CGColor]; 392 | square.strokeColor = [chartData.strokeColor CGColor]; 393 | square.lineWidth = 1; 394 | 395 | // Add to parent layer 396 | return square; 397 | } 398 | else { 399 | // you cann add your own scatter chart point here 400 | } 401 | return nil ; 402 | } 403 | 404 | - (void) drawLineFromPoint : (CGPoint) startPoint ToPoint : (CGPoint) endPoint WithLineWith : (CGFloat) lineWidth AndWithColor : (UIColor*) color{ 405 | 406 | // call the same method on a background thread 407 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 408 | if (self.displayAnimated) { 409 | [NSThread sleepForTimeInterval:2]; 410 | } 411 | // calculating start and end point 412 | __block CGFloat startX = [self mappingIsForAxisX:true WithValue:startPoint.x]; 413 | __block CGFloat startY = [self mappingIsForAxisX:false WithValue:startPoint.y]; 414 | __block CGFloat endX = [self mappingIsForAxisX:true WithValue:endPoint.x]; 415 | __block CGFloat endY = [self mappingIsForAxisX:false WithValue:endPoint.y]; 416 | // update UI on the main thread 417 | dispatch_async(dispatch_get_main_queue(), ^{ 418 | // drawing path between two points 419 | UIBezierPath *path = [UIBezierPath bezierPath]; 420 | [path moveToPoint:CGPointMake(startX, startY)]; 421 | [path addLineToPoint:CGPointMake(endX, endY)]; 422 | CAShapeLayer *shapeLayer = [CAShapeLayer layer]; 423 | shapeLayer.path = [path CGPath]; 424 | shapeLayer.strokeColor = [color CGColor]; 425 | shapeLayer.lineWidth = lineWidth; 426 | shapeLayer.fillColor = [color CGColor]; 427 | // adding animation to path 428 | [self addStrokeEndAnimationIfNeededToLayer:shapeLayer]; 429 | [self.layer addSublayer:shapeLayer]; 430 | }); 431 | }); 432 | } 433 | 434 | - (void)addStrokeEndAnimationIfNeededToLayer:(CAShapeLayer *)shapeLayer{ 435 | 436 | if (self.displayAnimated) { 437 | CABasicAnimation *animateStrokeEnd = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 438 | animateStrokeEnd.duration = _duration; 439 | animateStrokeEnd.fromValue = [NSNumber numberWithFloat:0.0f]; 440 | animateStrokeEnd.toValue = [NSNumber numberWithFloat:1.0f]; 441 | [shapeLayer addAnimation:animateStrokeEnd forKey:nil]; 442 | } 443 | } 444 | 445 | @end 446 | -------------------------------------------------------------------------------- /PNChart/PNPieChart.m: -------------------------------------------------------------------------------- 1 | // 2 | // PNPieChart.m 3 | // PNChartDemo 4 | // 5 | // Created by Hang Zhang on 14-5-5. 6 | // Copyright (c) 2014年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PNPieChart.h" 10 | //needed for the expected label size 11 | #import "PNLineChart.h" 12 | 13 | @interface PNPieChart() 14 | 15 | @property (nonatomic) NSArray *items; 16 | @property (nonatomic) NSArray *endPercentages; 17 | 18 | @property (nonatomic) UIView *contentView; 19 | @property (nonatomic) CAShapeLayer *pieLayer; 20 | @property (nonatomic) NSMutableArray *descriptionLabels; 21 | @property (strong, nonatomic) CAShapeLayer *sectorHighlight; 22 | 23 | @property (nonatomic, strong) NSMutableDictionary *selectedItems; 24 | 25 | - (void)loadDefault; 26 | 27 | - (UILabel *)descriptionLabelForItemAtIndex:(NSUInteger)index; 28 | - (PNPieChartDataItem *)dataItemForIndex:(NSUInteger)index; 29 | - (CGFloat)startPercentageForItemAtIndex:(NSUInteger)index; 30 | - (CGFloat)endPercentageForItemAtIndex:(NSUInteger)index; 31 | - (CGFloat)ratioForItemAtIndex:(NSUInteger)index; 32 | 33 | - (CAShapeLayer *)newCircleLayerWithRadius:(CGFloat)radius 34 | borderWidth:(CGFloat)borderWidth 35 | fillColor:(UIColor *)fillColor 36 | borderColor:(UIColor *)borderColor 37 | startPercentage:(CGFloat)startPercentage 38 | endPercentage:(CGFloat)endPercentage; 39 | 40 | 41 | @end 42 | 43 | 44 | @implementation PNPieChart 45 | 46 | -(id)initWithFrame:(CGRect)frame items:(NSArray *)items{ 47 | self = [self initWithFrame:frame]; 48 | if(self){ 49 | _items = [NSArray arrayWithArray:items]; 50 | [self baseInit]; 51 | } 52 | 53 | return self; 54 | } 55 | 56 | - (void)awakeFromNib{ 57 | [super awakeFromNib]; 58 | [self baseInit]; 59 | } 60 | 61 | - (void)baseInit{ 62 | _selectedItems = [NSMutableDictionary dictionary]; 63 | //在绘制圆形时,应当考虑矩形的宽和高的大小问题,当宽大于高时,绘制饼图时,会超出整个view的范围,因此建议在此处进行判断 64 | 65 | CGFloat minimal = (CGRectGetWidth(self.bounds) < CGRectGetHeight(self.bounds)) ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds); 66 | 67 | _outerCircleRadius = minimal / 2; 68 | _innerCircleRadius = minimal / 6; 69 | // _outerCircleRadius = CGRectGetWidth(self.bounds) / 2; 70 | // _innerCircleRadius = CGRectGetWidth(self.bounds) / 6; 71 | _descriptionTextColor = [UIColor whiteColor]; 72 | _descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:18.0]; 73 | _descriptionTextShadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; 74 | _descriptionTextShadowOffset = CGSizeMake(0, 1); 75 | _duration = 1.0; 76 | _shouldHighlightSectorOnTouch = YES; 77 | _enableMultipleSelection = NO; 78 | _hideValues = NO; 79 | 80 | [super setupDefaultValues]; 81 | [self loadDefault]; 82 | } 83 | 84 | - (void)loadDefault{ 85 | __block CGFloat currentTotal = 0; 86 | CGFloat total = [[self.items valueForKeyPath:@"@sum.value"] floatValue]; 87 | NSMutableArray *endPercentages = [NSMutableArray new]; 88 | [_items enumerateObjectsUsingBlock:^(PNPieChartDataItem *item, NSUInteger idx, BOOL *stop) { 89 | if (total == 0){ 90 | [endPercentages addObject:@(1.0 / _items.count * (idx + 1))]; 91 | }else{ 92 | currentTotal += item.value; 93 | [endPercentages addObject:@(currentTotal / total)]; 94 | } 95 | }]; 96 | self.endPercentages = [endPercentages copy]; 97 | 98 | [_contentView removeFromSuperview]; 99 | _contentView = [[UIView alloc] initWithFrame:self.bounds]; 100 | [self addSubview:_contentView]; 101 | _descriptionLabels = [NSMutableArray new]; 102 | 103 | _pieLayer = [CAShapeLayer layer]; 104 | [_contentView.layer addSublayer:_pieLayer]; 105 | 106 | } 107 | 108 | /** Override this to change how inner attributes are computed. **/ 109 | - (void)recompute { 110 | 111 | //同理 112 | CGFloat minimal = (CGRectGetWidth(self.bounds) < CGRectGetHeight(self.bounds)) ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds); 113 | self.outerCircleRadius = minimal / 2; 114 | self.innerCircleRadius = minimal / 6; 115 | } 116 | 117 | #pragma mark - 118 | 119 | - (void)strokeChart{ 120 | [self loadDefault]; 121 | [self recompute]; 122 | 123 | PNPieChartDataItem *currentItem; 124 | for (int i = 0; i < _items.count; i++) { 125 | currentItem = [self dataItemForIndex:i]; 126 | 127 | 128 | CGFloat startPercentage = [self startPercentageForItemAtIndex:i]; 129 | CGFloat endPercentage = [self endPercentageForItemAtIndex:i]; 130 | 131 | CGFloat radius = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius) / 2; 132 | CGFloat borderWidth = _outerCircleRadius - _innerCircleRadius; 133 | 134 | CAShapeLayer *currentPieLayer = [self newCircleLayerWithRadius:radius 135 | borderWidth:borderWidth 136 | fillColor:[UIColor clearColor] 137 | borderColor:currentItem.color 138 | startPercentage:startPercentage 139 | endPercentage:endPercentage]; 140 | [_pieLayer addSublayer:currentPieLayer]; 141 | } 142 | 143 | [self maskChart]; 144 | 145 | for (int i = 0; i < _items.count; i++) { 146 | UILabel *descriptionLabel = [self descriptionLabelForItemAtIndex:i]; 147 | [_contentView addSubview:descriptionLabel]; 148 | [_descriptionLabels addObject:descriptionLabel]; 149 | } 150 | 151 | [self addAnimationIfNeeded]; 152 | } 153 | 154 | - (UILabel *)descriptionLabelForItemAtIndex:(NSUInteger)index{ 155 | PNPieChartDataItem *currentDataItem = [self dataItemForIndex:index]; 156 | CGFloat distance = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius) / 2; 157 | CGFloat centerPercentage = ([self startPercentageForItemAtIndex:index] + [self endPercentageForItemAtIndex:index])/ 2; 158 | CGFloat rad = centerPercentage * 2 * M_PI; 159 | 160 | UILabel *descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 80)]; 161 | NSString *titleText = currentDataItem.textDescription; 162 | 163 | NSString *titleValue; 164 | 165 | if (self.showAbsoluteValues) { 166 | titleValue = [NSString stringWithFormat:@"%.0f",currentDataItem.value]; 167 | }else{ 168 | titleValue = [NSString stringWithFormat:@"%.0f%%",[self ratioForItemAtIndex:index] * 100]; 169 | } 170 | 171 | if (self.hideValues) 172 | descriptionLabel.text = titleText; 173 | else if(!titleText || self.showOnlyValues) 174 | descriptionLabel.text = titleValue; 175 | else { 176 | NSString* str = [titleValue stringByAppendingString:[NSString stringWithFormat:@"\n%@",titleText]]; 177 | descriptionLabel.text = str ; 178 | } 179 | 180 | //If value is less than cutoff, show no label 181 | if ([self ratioForItemAtIndex:index] < self.labelPercentageCutoff ) 182 | { 183 | descriptionLabel.text = nil; 184 | } 185 | 186 | CGPoint center = CGPointMake(_outerCircleRadius + distance * sin(rad), 187 | _outerCircleRadius - distance * cos(rad)); 188 | 189 | descriptionLabel.font = _descriptionTextFont; 190 | CGSize labelSize = [descriptionLabel.text sizeWithAttributes:@{NSFontAttributeName:descriptionLabel.font}]; 191 | descriptionLabel.frame = CGRectMake(descriptionLabel.frame.origin.x, descriptionLabel.frame.origin.y, 192 | descriptionLabel.frame.size.width, labelSize.height); 193 | descriptionLabel.numberOfLines = 0; 194 | descriptionLabel.textColor = _descriptionTextColor; 195 | descriptionLabel.shadowColor = _descriptionTextShadowColor; 196 | descriptionLabel.shadowOffset = _descriptionTextShadowOffset; 197 | descriptionLabel.textAlignment = NSTextAlignmentCenter; 198 | descriptionLabel.center = center; 199 | descriptionLabel.alpha = 0; 200 | descriptionLabel.backgroundColor = [UIColor clearColor]; 201 | return descriptionLabel; 202 | } 203 | 204 | - (void)updateChartData:(NSArray *)items { 205 | self.items = items; 206 | } 207 | 208 | - (PNPieChartDataItem *)dataItemForIndex:(NSUInteger)index{ 209 | return self.items[index]; 210 | } 211 | 212 | - (CGFloat)startPercentageForItemAtIndex:(NSUInteger)index{ 213 | if(index == 0){ 214 | return 0; 215 | } 216 | 217 | return [_endPercentages[index - 1] floatValue]; 218 | } 219 | 220 | - (CGFloat)endPercentageForItemAtIndex:(NSUInteger)index{ 221 | return [_endPercentages[index] floatValue]; 222 | } 223 | 224 | - (CGFloat)ratioForItemAtIndex:(NSUInteger)index{ 225 | return [self endPercentageForItemAtIndex:index] - [self startPercentageForItemAtIndex:index]; 226 | } 227 | 228 | #pragma mark private methods 229 | 230 | - (CAShapeLayer *)newCircleLayerWithRadius:(CGFloat)radius 231 | borderWidth:(CGFloat)borderWidth 232 | fillColor:(UIColor *)fillColor 233 | borderColor:(UIColor *)borderColor 234 | startPercentage:(CGFloat)startPercentage 235 | endPercentage:(CGFloat)endPercentage{ 236 | CAShapeLayer *circle = [CAShapeLayer layer]; 237 | 238 | CGPoint center = CGPointMake(CGRectGetMidX(self.bounds),CGRectGetMidY(self.bounds)); 239 | 240 | UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center 241 | radius:radius 242 | startAngle:-M_PI_2 243 | endAngle:M_PI_2 * 3 244 | clockwise:YES]; 245 | 246 | circle.fillColor = fillColor.CGColor; 247 | circle.strokeColor = borderColor.CGColor; 248 | circle.strokeStart = startPercentage; 249 | circle.strokeEnd = endPercentage; 250 | circle.lineWidth = borderWidth; 251 | circle.path = path.CGPath; 252 | 253 | return circle; 254 | } 255 | 256 | - (void)maskChart{ 257 | CGFloat radius = _innerCircleRadius + (_outerCircleRadius - _innerCircleRadius) / 2; 258 | CGFloat borderWidth = _outerCircleRadius - _innerCircleRadius; 259 | CAShapeLayer *maskLayer = [self newCircleLayerWithRadius:radius 260 | borderWidth:borderWidth 261 | fillColor:[UIColor clearColor] 262 | borderColor:[UIColor blackColor] 263 | startPercentage:0 264 | endPercentage:1]; 265 | 266 | _pieLayer.mask = maskLayer; 267 | } 268 | 269 | - (void)addAnimationIfNeeded{ 270 | if (self.displayAnimated) { 271 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; 272 | animation.duration = _duration; 273 | animation.fromValue = @0; 274 | animation.toValue = @1; 275 | animation.delegate = self; 276 | animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 277 | animation.removedOnCompletion = YES; 278 | [_pieLayer.mask addAnimation:animation forKey:@"circleAnimation"]; 279 | } 280 | else { 281 | // Add description labels since no animation is required 282 | [_descriptionLabels enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 283 | [obj setAlpha:1]; 284 | }]; 285 | } 286 | } 287 | 288 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{ 289 | [_descriptionLabels enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 290 | [UIView animateWithDuration:0.2 animations:^(){ 291 | [obj setAlpha:1]; 292 | }]; 293 | }]; 294 | } 295 | 296 | - (void)didTouchAt:(CGPoint)touchLocation 297 | { 298 | CGPoint circleCenter = CGPointMake(_contentView.bounds.size.width/2, _contentView.bounds.size.height/2); 299 | 300 | CGFloat distanceFromCenter = sqrtf(powf((touchLocation.y - circleCenter.y),2) + powf((touchLocation.x - circleCenter.x),2)); 301 | 302 | if (distanceFromCenter < _innerCircleRadius) { 303 | if ([self.delegate respondsToSelector:@selector(didUnselectPieItem)]) { 304 | [self.delegate didUnselectPieItem]; 305 | } 306 | [self.sectorHighlight removeFromSuperlayer]; 307 | return; 308 | } 309 | 310 | CGFloat percentage = [self findPercentageOfAngleInCircle:circleCenter fromPoint:touchLocation]; 311 | int index = 0; 312 | while (percentage > [self endPercentageForItemAtIndex:index]) { 313 | index ++; 314 | } 315 | 316 | if ([self.delegate respondsToSelector:@selector(userClickedOnPieIndexItem:)]) { 317 | [self.delegate userClickedOnPieIndexItem:index]; 318 | } 319 | 320 | if (self.shouldHighlightSectorOnTouch) 321 | { 322 | if (!self.enableMultipleSelection) 323 | { 324 | if (self.sectorHighlight) 325 | [self.sectorHighlight removeFromSuperlayer]; 326 | } 327 | 328 | PNPieChartDataItem *currentItem = [self dataItemForIndex:index]; 329 | 330 | CGFloat red,green,blue,alpha; 331 | UIColor *old = currentItem.color; 332 | [old getRed:&red green:&green blue:&blue alpha:&alpha]; 333 | alpha /= 2; 334 | UIColor *newColor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha]; 335 | 336 | CGFloat startPercentage = [self startPercentageForItemAtIndex:index]; 337 | CGFloat endPercentage = [self endPercentageForItemAtIndex:index]; 338 | 339 | self.sectorHighlight = [self newCircleLayerWithRadius:_outerCircleRadius + 5 340 | borderWidth:10 341 | fillColor:[UIColor clearColor] 342 | borderColor:newColor 343 | startPercentage:startPercentage 344 | endPercentage:endPercentage]; 345 | 346 | if (self.enableMultipleSelection) 347 | { 348 | NSString *dictIndex = [NSString stringWithFormat:@"%d", index]; 349 | CAShapeLayer *indexShape = [self.selectedItems valueForKey:dictIndex]; 350 | if (indexShape) 351 | { 352 | [indexShape removeFromSuperlayer]; 353 | [self.selectedItems removeObjectForKey:dictIndex]; 354 | } 355 | else 356 | { 357 | [self.selectedItems setObject:self.sectorHighlight forKey:dictIndex]; 358 | [_contentView.layer addSublayer:self.sectorHighlight]; 359 | } 360 | } 361 | else 362 | { 363 | [_contentView.layer addSublayer:self.sectorHighlight]; 364 | } 365 | } 366 | } 367 | 368 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 369 | { 370 | for (UITouch *touch in touches) { 371 | CGPoint touchLocation = [touch locationInView:_contentView]; 372 | [self didTouchAt:touchLocation]; 373 | } 374 | } 375 | 376 | - (CGFloat) findPercentageOfAngleInCircle:(CGPoint)center fromPoint:(CGPoint)reference{ 377 | //Find angle of line Passing In Reference And Center 378 | CGFloat angleOfLine = atanf((reference.y - center.y) / (reference.x - center.x)); 379 | CGFloat percentage = (angleOfLine + M_PI/2)/(2 * M_PI); 380 | return (reference.x - center.x) > 0 ? percentage : percentage + .5; 381 | } 382 | 383 | - (UIView*) getLegendWithMaxWidth:(CGFloat)mWidth{ 384 | if ([self.items count] < 1) { 385 | return nil; 386 | } 387 | 388 | /* This is a small circle that refers to the chart data */ 389 | CGFloat legendCircle = 16; 390 | 391 | CGFloat hSpacing = 0; 392 | 393 | CGFloat beforeLabel = legendCircle + hSpacing; 394 | 395 | /* x and y are the coordinates of the starting point of each legend item */ 396 | CGFloat x = 0; 397 | CGFloat y = 0; 398 | 399 | /* accumulated width and height */ 400 | CGFloat totalWidth = 0; 401 | CGFloat totalHeight = 0; 402 | 403 | NSMutableArray *legendViews = [[NSMutableArray alloc] init]; 404 | 405 | /* Determine the max width of each legend item */ 406 | CGFloat maxLabelWidth; 407 | if (self.legendStyle == PNLegendItemStyleStacked) { 408 | maxLabelWidth = mWidth - beforeLabel; 409 | }else{ 410 | maxLabelWidth = MAXFLOAT; 411 | } 412 | 413 | /* this is used when labels wrap text and the line 414 | * should be in the middle of the first row */ 415 | CGFloat singleRowHeight = [PNLineChart sizeOfString:@"Test" 416 | withWidth:MAXFLOAT 417 | font:self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]].height; 418 | 419 | NSUInteger counter = 0; 420 | NSUInteger rowWidth = 0; 421 | NSUInteger rowMaxHeight = 0; 422 | 423 | for (PNPieChartDataItem *pdata in self.items) { 424 | if (!pdata.textDescription) { 425 | break; 426 | } 427 | /* Expected label size*/ 428 | CGSize labelsize = [PNLineChart sizeOfString:pdata.textDescription 429 | withWidth:maxLabelWidth 430 | font:self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]]; 431 | 432 | if ((rowWidth + labelsize.width + beforeLabel > mWidth)&&(self.legendStyle == PNLegendItemStyleSerial)) { 433 | rowWidth = 0; 434 | x = 0; 435 | y += rowMaxHeight; 436 | rowMaxHeight = 0; 437 | } 438 | rowWidth += labelsize.width + beforeLabel; 439 | totalWidth = self.legendStyle == PNLegendItemStyleSerial ? fmaxf(rowWidth, totalWidth) : fmaxf(totalWidth, labelsize.width + beforeLabel); 440 | // Add inflexion type 441 | [legendViews addObject:[self drawInflexion:legendCircle * .6 442 | center:CGPointMake(x + legendCircle / 2, y + singleRowHeight / 2) 443 | andColor:pdata.color]]; 444 | 445 | 446 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x + beforeLabel, y, labelsize.width, labelsize.height)]; 447 | label.text = pdata.textDescription; 448 | label.textColor = self.legendFontColor ? self.legendFontColor : [UIColor blackColor]; 449 | label.font = self.legendFont ? self.legendFont : [UIFont systemFontOfSize:12.0f]; 450 | label.lineBreakMode = NSLineBreakByWordWrapping; 451 | label.numberOfLines = 0; 452 | 453 | 454 | rowMaxHeight = fmaxf(rowMaxHeight, labelsize.height); 455 | x += self.legendStyle == PNLegendItemStyleStacked ? 0 : labelsize.width + beforeLabel; 456 | y += self.legendStyle == PNLegendItemStyleStacked ? labelsize.height : 0; 457 | 458 | 459 | totalHeight = self.legendStyle == PNLegendItemStyleSerial ? fmaxf(totalHeight, rowMaxHeight + y) : totalHeight + labelsize.height; 460 | [legendViews addObject:label]; 461 | counter ++; 462 | } 463 | 464 | UIView *legend = [[UIView alloc] initWithFrame:CGRectMake(0, 0, totalWidth, totalHeight)]; 465 | 466 | for (UIView* v in legendViews) { 467 | [legend addSubview:v]; 468 | } 469 | return legend; 470 | } 471 | 472 | 473 | - (UIImageView*)drawInflexion:(CGFloat)size center:(CGPoint)center andColor:(UIColor*)color 474 | { 475 | //Make the size a little bigger so it includes also border stroke 476 | CGSize aSize = CGSizeMake(size, size); 477 | 478 | 479 | UIGraphicsBeginImageContextWithOptions(aSize, NO, 0.0); 480 | CGContextRef context = UIGraphicsGetCurrentContext(); 481 | 482 | CGContextAddArc(context, size/2, size/ 2, size/2, 0, M_PI*2, YES); 483 | 484 | 485 | //Set some fill color 486 | CGContextSetFillColorWithColor(context, color.CGColor); 487 | 488 | //Finally draw 489 | CGContextDrawPath(context, kCGPathFill); 490 | 491 | //now get the image from the context 492 | UIImage *squareImage = UIGraphicsGetImageFromCurrentImageContext(); 493 | 494 | UIGraphicsEndImageContext(); 495 | 496 | //// Translate origin 497 | CGFloat originX = center.x - (size) / 2.0; 498 | CGFloat originY = center.y - (size) / 2.0; 499 | 500 | UIImageView *squareImageView = [[UIImageView alloc]initWithImage:squareImage]; 501 | [squareImageView setFrame:CGRectMake(originX, originY, size, size)]; 502 | return squareImageView; 503 | } 504 | 505 | /* Redraw the chart on autolayout */ 506 | -(void)layoutSubviews { 507 | [super layoutSubviews]; 508 | [self strokeChart]; 509 | } 510 | 511 | @end 512 | -------------------------------------------------------------------------------- /PNChartDemo/PCChartViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PCChartViewController.m 3 | // PNChartDemo 4 | // 5 | // Created by kevin on 11/7/13. 6 | // Copyright (c) 2013年 kevinzhow. All rights reserved. 7 | // 8 | 9 | #import "PCChartViewController.h" 10 | 11 | #define ARC4RANDOM_MAX 0x100000000 12 | 13 | @implementation PCChartViewController 14 | 15 | 16 | - (void)viewDidLoad { 17 | [super viewDidLoad]; 18 | self.titleLabel.textColor = PNFreshGreen; 19 | self.leftSwitch.hidden = YES; 20 | self.rightSwitch.hidden = YES; 21 | self.leftLabel.hidden = YES; 22 | self.rightLabel.hidden = YES; 23 | self.centerSwitch.hidden = YES; 24 | self.centerSwitchLabel.hidden = YES; 25 | 26 | self.changeValueButton.hidden = YES; 27 | 28 | if ([self.title isEqualToString:@"Line Chart"]) { 29 | 30 | self.titleLabel.text = @"Line Chart"; 31 | 32 | self.rightSwitch.hidden = NO; 33 | self.rightLabel.hidden = NO; 34 | self.leftSwitch.hidden = NO; 35 | self.leftLabel.hidden = NO; 36 | self.animationsSwitch.hidden = NO; 37 | 38 | self.leftLabel.text = @"Dark Background"; 39 | self.rightLabel.text = @"Show Curved Lines"; 40 | 41 | self.animationsSwitch.enabled = YES; 42 | self.rightSwitch.enabled = YES; 43 | self.leftSwitch.enabled = YES; 44 | [self.animationsSwitch setOn:NO]; 45 | [self.rightSwitch setOn:NO]; 46 | [self.leftSwitch setOn:NO]; 47 | 48 | 49 | self.lineChart.backgroundColor = [UIColor whiteColor]; 50 | self.lineChart.yGridLinesColor = [UIColor grayColor]; 51 | [self.lineChart.chartData enumerateObjectsUsingBlock:^(PNLineChartData *obj, NSUInteger idx, BOOL *stop) { 52 | obj.pointLabelColor = [UIColor blackColor]; 53 | }]; 54 | 55 | self.lineChart = [[PNLineChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 200.0)]; 56 | self.lineChart.showCoordinateAxis = YES; 57 | self.lineChart.yLabelFormat = @"%1.1f"; 58 | self.lineChart.xLabelFont = [UIFont fontWithName:@"Helvetica-Light" size:8.0]; 59 | // [self.lineChart setXLabels:@[@"SEP 1", @"SEP 2", @"SEP 3", @"SEP 4", @"SEP 5", @"SEP 6", @"SEP 7"]]; 60 | [self.lineChart setXLabels:@[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10", @"11"]]; 61 | self.lineChart.yLabelColor = [UIColor blackColor]; 62 | self.lineChart.xLabelColor = [UIColor blackColor]; 63 | 64 | // added an example to show how yGridLines can be enabled 65 | // the color is set to clearColor so that the demo remains the same 66 | self.lineChart.showGenYLabels = NO; 67 | self.lineChart.showYGridLines = YES; 68 | 69 | //Use yFixedValueMax and yFixedValueMin to Fix the Max and Min Y Value 70 | //Only if you needed 71 | self.lineChart.yFixedValueMax = 200; 72 | self.lineChart.yFixedValueMin = 0.0; 73 | 74 | [self.lineChart setYLabels:@[ 75 | @"0", 76 | @"50", 77 | @"100", 78 | @"150", 79 | @"200", 80 | @"250", 81 | @"300", 82 | ] 83 | ]; 84 | 85 | // Line Chart #1 86 | NSArray *data01Array = @[@124, @134, @128, @127, @144, @139, @134, @134, @132, @130, @116, @95]; 87 | PNLineChartData *data01 = [PNLineChartData new]; 88 | 89 | data01.rangeColors = @[ 90 | [[PNLineChartColorRange alloc] initWithRange:NSMakeRange(1, 54) color:[UIColor redColor]], 91 | [[PNLineChartColorRange alloc] initWithRange:NSMakeRange(55, 6) color:[UIColor yellowColor]], 92 | [[PNLineChartColorRange alloc] initWithRange:NSMakeRange(61, 49) color:[UIColor greenColor]], 93 | [[PNLineChartColorRange alloc] initWithRange:NSMakeRange(110, 20) color:[UIColor yellowColor]], 94 | [[PNLineChartColorRange alloc] initWithRange:NSMakeRange(130, 470) color:[UIColor redColor]] 95 | ]; 96 | data01.dataTitle = @"Alpha"; 97 | data01.color = PNFreshGreen; 98 | data01.pointLabelColor = [UIColor blackColor]; 99 | data01.alpha = 0.3f; 100 | data01.showPointLabel = YES; 101 | data01.pointLabelFont = [UIFont fontWithName:@"Helvetica-Light" size:9.0]; 102 | data01.itemCount = data01Array.count; 103 | data01.inflexionPointColor = PNRed; 104 | data01.inflexionPointStyle = PNLineChartPointStyleTriangle; 105 | data01.getData = ^(NSUInteger index) { 106 | CGFloat yValue = [data01Array[index] floatValue]; 107 | return [PNLineChartDataItem dataItemWithY:yValue]; 108 | }; 109 | 110 | // Line Chart #2 111 | NSArray *data02Array = @[@0.0, @18.1, @26.4, @30.2, @12.2, @16.2, @27.2]; 112 | PNLineChartData *data02 = [PNLineChartData new]; 113 | data02.dataTitle = @"Beta"; 114 | data02.pointLabelColor = [UIColor blackColor]; 115 | data02.color = PNTwitterColor; 116 | data02.alpha = 0.5f; 117 | data02.itemCount = data02Array.count; 118 | data02.inflexionPointStyle = PNLineChartPointStyleCircle; 119 | data02.getData = ^(NSUInteger index) { 120 | CGFloat yValue = [data02Array[index] floatValue]; 121 | return [PNLineChartDataItem dataItemWithY:yValue]; 122 | }; 123 | 124 | self.lineChart.chartData = @[data01, data02]; 125 | [self.lineChart.chartData enumerateObjectsUsingBlock:^(PNLineChartData *obj, NSUInteger idx, BOOL *stop) { 126 | obj.pointLabelColor = [UIColor blackColor]; 127 | }]; 128 | 129 | 130 | [self.lineChart strokeChart]; 131 | self.lineChart.delegate = self; 132 | 133 | 134 | [self.view addSubview:self.lineChart]; 135 | 136 | self.lineChart.legendStyle = PNLegendItemStyleStacked; 137 | self.lineChart.legendFont = [UIFont boldSystemFontOfSize:12.0f]; 138 | self.lineChart.legendFontColor = [UIColor redColor]; 139 | 140 | UIView *legend = [self.lineChart getLegendWithMaxWidth:320]; 141 | [legend setFrame:CGRectMake(30, 340, legend.frame.size.width, legend.frame.size.width)]; 142 | [self.view addSubview:legend]; 143 | } else if ([self.title isEqualToString:@"Bar Chart"]) { 144 | static NSNumberFormatter *barChartFormatter; 145 | if (!barChartFormatter) { 146 | barChartFormatter = [[NSNumberFormatter alloc] init]; 147 | barChartFormatter.numberStyle = NSNumberFormatterCurrencyStyle; 148 | barChartFormatter.allowsFloats = NO; 149 | barChartFormatter.maximumFractionDigits = 0; 150 | } 151 | self.titleLabel.text = @"Bar Chart"; 152 | 153 | self.barChart = [[PNBarChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 200.0)]; 154 | // self.barChart.showLabel = NO; 155 | self.barChart.yLabelFormatter = ^(CGFloat yValue) { 156 | return [barChartFormatter stringFromNumber:@(yValue)]; 157 | }; 158 | 159 | self.barChart.yChartLabelWidth = 20.0; 160 | self.barChart.chartMarginLeft = 30.0; 161 | self.barChart.chartMarginRight = 10.0; 162 | self.barChart.chartMarginTop = 5.0; 163 | self.barChart.chartMarginBottom = 10.0; 164 | 165 | 166 | self.barChart.labelMarginTop = 5.0; 167 | self.barChart.showChartBorder = YES; 168 | [self.barChart setXLabels:@[@"2", @"3", @"4", @"5", @"2", @"3", @"4", @"5"]]; 169 | // self.barChart.yLabels = @[@-10,@0,@10]; 170 | // [self.barChart setYValues:@[@10000.0,@30000.0,@10000.0,@100000.0,@500000.0,@1000000.0,@1150000.0,@2150000.0]]; 171 | [self.barChart setYValues:@[@10.82, @1.88, @6.96, @33.93, @10.82, @1.88, @6.96, @33.93]]; 172 | [self.barChart setStrokeColors:@[PNGreen, PNGreen, PNRed, PNGreen, PNGreen, PNGreen, PNRed, PNGreen]]; 173 | self.barChart.isGradientShow = NO; 174 | self.barChart.isShowNumbers = NO; 175 | 176 | [self.barChart strokeChart]; 177 | 178 | self.barChart.delegate = self; 179 | 180 | [self.view addSubview:self.barChart]; 181 | } else if ([self.title isEqualToString:@"Circle Chart"]) { 182 | self.titleLabel.text = @"Circle Chart"; 183 | 184 | 185 | self.circleChart = [[PNCircleChart alloc] initWithFrame:CGRectMake(0, 150.0, SCREEN_WIDTH, 100.0) 186 | total:@100 187 | current:@60 188 | clockwise:YES]; 189 | 190 | self.circleChart.backgroundColor = [UIColor whiteColor]; 191 | 192 | [self.circleChart setStrokeColor:[UIColor clearColor]]; 193 | [self.circleChart setStrokeColorGradientStart:[UIColor blueColor]]; 194 | [self.circleChart strokeChart]; 195 | 196 | [self.view addSubview:self.circleChart]; 197 | } else if ([self.title isEqualToString:@"Pie Chart"]) { 198 | self.titleLabel.text = @"Pie Chart"; 199 | self.leftSwitch.hidden = NO; 200 | self.rightSwitch.hidden = NO; 201 | self.leftLabel.hidden = NO; 202 | self.rightLabel.hidden = NO; 203 | self.centerSwitch.hidden = NO; 204 | self.centerSwitchLabel.hidden = NO; 205 | 206 | 207 | NSArray *items = @[[PNPieChartDataItem dataItemWithValue:10 color:PNLightGreen], 208 | [PNPieChartDataItem dataItemWithValue:20 color:PNFreshGreen description:@"WWDC"], 209 | [PNPieChartDataItem dataItemWithValue:40 color:PNDeepGreen description:@"GOOG I/O"], 210 | ]; 211 | 212 | self.pieChart = [[PNPieChart alloc] initWithFrame:CGRectMake((CGFloat) (SCREEN_WIDTH / 2.0 - 100), 135, 200.0, 200.0) items:items]; 213 | self.pieChart.descriptionTextColor = [UIColor whiteColor]; 214 | self.pieChart.descriptionTextFont = [UIFont fontWithName:@"Avenir-Medium" size:11.0]; 215 | self.pieChart.descriptionTextShadowColor = [UIColor clearColor]; 216 | self.pieChart.showAbsoluteValues = NO; 217 | self.pieChart.showOnlyValues = NO; 218 | [self.pieChart strokeChart]; 219 | 220 | 221 | self.pieChart.legendStyle = PNLegendItemStyleStacked; 222 | self.pieChart.legendFont = [UIFont boldSystemFontOfSize:12.0f]; 223 | 224 | UIView *legend = [self.pieChart getLegendWithMaxWidth:200]; 225 | [legend setFrame:CGRectMake(130, 350, legend.frame.size.width, legend.frame.size.height)]; 226 | [self.view addSubview:legend]; 227 | 228 | [self.view addSubview:self.pieChart]; 229 | self.changeValueButton.hidden = YES; 230 | } else if ([self.title isEqualToString:@"Scatter Chart"]) { 231 | self.animationsSwitch.hidden = YES; 232 | self.animationsLabel.hidden = YES; 233 | 234 | self.titleLabel.text = @"Scatter Chart"; 235 | 236 | self.scatterChart = [[PNScatterChart alloc] initWithFrame:CGRectMake((CGFloat) (SCREEN_WIDTH / 6.0 - 30), 135, 280, 200)]; 237 | // self.scatterChart.yLabelFormat = @"xxx %1.1f"; 238 | [self.scatterChart setAxisXWithMinimumValue:20 andMaxValue:100 toTicks:6]; 239 | [self.scatterChart setAxisYWithMinimumValue:30 andMaxValue:50 toTicks:5]; 240 | [self.scatterChart setAxisXLabel:@[@"x1", @"x2", @"x3", @"x4", @"x5", @"x6"]]; 241 | [self.scatterChart setAxisYLabel:@[@"y1", @"y2", @"y3", @"y4", @"y5"]]; 242 | 243 | NSArray *data01Array = [self randomSetOfObjects]; 244 | PNScatterChartData *data01 = [PNScatterChartData new]; 245 | data01.strokeColor = PNGreen; 246 | data01.fillColor = PNFreshGreen; 247 | data01.size = 2; 248 | data01.itemCount = [data01Array[0] count]; 249 | data01.inflexionPointStyle = PNScatterChartPointStyleCircle; 250 | __block NSMutableArray *XAr1 = [NSMutableArray arrayWithArray:data01Array[0]]; 251 | __block NSMutableArray *YAr1 = [NSMutableArray arrayWithArray:data01Array[1]]; 252 | 253 | data01.getData = ^(NSUInteger index) { 254 | CGFloat xValue; 255 | xValue = [XAr1[index] floatValue]; 256 | CGFloat yValue = [YAr1[index] floatValue]; 257 | return [PNScatterChartDataItem dataItemWithX:xValue AndWithY:yValue]; 258 | }; 259 | 260 | [self.scatterChart setup]; 261 | self.scatterChart.chartData = @[data01]; 262 | /*** 263 | this is for drawing line to compare 264 | CGPoint start = CGPointMake(20, 35); 265 | CGPoint end = CGPointMake(80, 45); 266 | [self.scatterChart drawLineFromPoint:start ToPoint:end WithLineWith:2 AndWithColor:PNBlack]; 267 | ***/ 268 | self.scatterChart.delegate = self; 269 | self.changeValueButton.hidden = YES; 270 | [self.view addSubview:self.scatterChart]; 271 | } else if ([self.title isEqualToString:@"Radar Chart"]) { 272 | self.titleLabel.text = @"Radar Chart"; 273 | 274 | self.leftSwitch.hidden = NO; 275 | self.rightSwitch.hidden = NO; 276 | self.leftLabel.hidden = NO; 277 | self.rightLabel.hidden = NO; 278 | self.leftLabel.text = @"Labels Style"; 279 | self.rightLabel.text = @"Graduation"; 280 | 281 | 282 | NSArray *items = @[[PNRadarChartDataItem dataItemWithValue:3 description:@"Art"], 283 | [PNRadarChartDataItem dataItemWithValue:2 description:@"Math"], 284 | [PNRadarChartDataItem dataItemWithValue:8 description:@"Sports"], 285 | [PNRadarChartDataItem dataItemWithValue:5 description:@"Literature"], 286 | [PNRadarChartDataItem dataItemWithValue:4 description:@"Other"], 287 | ]; 288 | self.radarChart = [[PNRadarChart alloc] initWithFrame:CGRectMake(0, 135.0, SCREEN_WIDTH, 300.0) items:items valueDivider:1]; 289 | 290 | self.radarChart.plotColor = [UIColor redColor]; 291 | 292 | [self.radarChart strokeChart]; 293 | 294 | [self.view addSubview:self.radarChart]; 295 | } 296 | 297 | } 298 | 299 | 300 | - (void)userClickedOnLineKeyPoint:(CGPoint)point lineIndex:(NSInteger)lineIndex pointIndex:(NSInteger)pointIndex { 301 | NSLog(@"Click Key on line %f, %f line index is %d and point index is %d", point.x, point.y, (int) lineIndex, (int) pointIndex); 302 | } 303 | 304 | - (void)userClickedOnLinePoint:(CGPoint)point lineIndex:(NSInteger)lineIndex { 305 | NSLog(@"Click on line %f, %f, line index is %d", point.x, point.y, (int) lineIndex); 306 | } 307 | 308 | 309 | - (IBAction)changeValue:(id)sender { 310 | 311 | if ([self.title isEqualToString:@"Line Chart"]) { 312 | 313 | // Line Chart #1 314 | NSArray *data01Array = @[@(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300)]; 315 | PNLineChartData *data01 = [PNLineChartData new]; 316 | data01.color = PNFreshGreen; 317 | data01.itemCount = data01Array.count; 318 | data01.inflexionPointColor = PNRed; 319 | data01.inflexionPointStyle = PNLineChartPointStyleTriangle; 320 | data01.getData = ^(NSUInteger index) { 321 | CGFloat yValue = [data01Array[index] floatValue]; 322 | return [PNLineChartDataItem dataItemWithY:yValue]; 323 | }; 324 | 325 | // Line Chart #2 326 | NSArray *data02Array = @[@(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300), @(arc4random() % 300)]; 327 | PNLineChartData *data02 = [PNLineChartData new]; 328 | data02.color = PNTwitterColor; 329 | data02.itemCount = data02Array.count; 330 | data02.inflexionPointStyle = PNLineChartPointStyleSquare; 331 | data02.getData = ^(NSUInteger index) { 332 | CGFloat yValue = [data02Array[index] floatValue]; 333 | return [PNLineChartDataItem dataItemWithY:yValue]; 334 | }; 335 | 336 | [self.lineChart setXLabels:@[@"DEC 1", @"DEC 2", @"DEC 3", @"DEC 4", @"DEC 5", @"DEC 6", @"DEC 7"]]; 337 | [self.lineChart updateChartData:@[data01, data02]]; 338 | 339 | } else if ([self.title isEqualToString:@"Bar Chart"]) { 340 | [self.barChart setXLabels:@[@"Jan 1", @"Jan 2", @"Jan 3", @"Jan 4", @"Jan 5", @"Jan 6", @"Jan 7"]]; 341 | [self.barChart updateChartData:@[@(arc4random() % 30), @(arc4random() % 30), @(arc4random() % 30), @(arc4random() % 30), @(arc4random() % 30), @(arc4random() % 30), @(arc4random() % 30)]]; 342 | } else if ([self.title isEqualToString:@"Circle Chart"]) { 343 | [self.circleChart updateChartByCurrent:@(arc4random() % 100)]; 344 | } else if ([self.title isEqualToString:@"Scatter Chart"]) { 345 | // will be code soon. 346 | } 347 | 348 | } 349 | 350 | - (void)userClickedOnBarAtIndex:(NSInteger)barIndex { 351 | 352 | NSLog(@"Click on bar %@", @(barIndex)); 353 | 354 | PNBar *bar = self.barChart.bars[(NSUInteger) barIndex]; 355 | 356 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; 357 | 358 | animation.fromValue = @1.0; 359 | animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 360 | animation.toValue = @1.1; 361 | animation.duration = 0.2; 362 | animation.repeatCount = 0; 363 | animation.autoreverses = YES; 364 | animation.removedOnCompletion = YES; 365 | animation.fillMode = kCAFillModeForwards; 366 | 367 | [bar.layer addAnimation:animation forKey:@"Float"]; 368 | } 369 | 370 | /* this function is used only for creating random points */ 371 | - (NSArray *)randomSetOfObjects { 372 | NSMutableArray *array = [NSMutableArray array]; 373 | NSString *LabelFormat = @"%1.f"; 374 | NSMutableArray *XAr = [NSMutableArray array]; 375 | NSMutableArray *YAr = [NSMutableArray array]; 376 | for (int i = 0; i < 25; i++) { 377 | [XAr addObject:[NSString stringWithFormat:LabelFormat, (((double) arc4random() / ARC4RANDOM_MAX) * (self.scatterChart.AxisX_maxValue - self.scatterChart.AxisX_minValue) + self.scatterChart.AxisX_minValue)]]; 378 | [YAr addObject:[NSString stringWithFormat:LabelFormat, (((double) arc4random() / ARC4RANDOM_MAX) * (self.scatterChart.AxisY_maxValue - self.scatterChart.AxisY_minValue) + self.scatterChart.AxisY_minValue)]]; 379 | } 380 | [array addObject:XAr]; 381 | [array addObject:YAr]; 382 | return array; 383 | } 384 | 385 | - (IBAction)rightSwitchChanged:(id)sender { 386 | if ([self.title isEqualToString:@"Pie Chart"]) { 387 | UISwitch *showLabels = (UISwitch *) sender; 388 | self.pieChart.showOnlyValues = !showLabels.on; 389 | [self.pieChart strokeChart]; 390 | } 391 | if ([self.title isEqualToString:@"Radar Chart"]) { 392 | UISwitch *showLabels = (UISwitch *) sender; 393 | self.radarChart.isShowGraduation = !showLabels.on; 394 | [self.radarChart strokeChart]; 395 | } else if ([self.title isEqualToString:@"Line Chart"]) { 396 | UISwitch *showLabels = (UISwitch *) sender; 397 | self.lineChart.showSmoothLines = showLabels.on; 398 | // NSLog(@"self.lineChart.showSmoothLines : %d", self.lineChart.showSmoothLines); 399 | [self.lineChart strokeChart]; 400 | } 401 | } 402 | 403 | - (IBAction)centerSwitchChanged:(id)sender { 404 | if (self.pieChart) { 405 | [self.pieChart setEnableMultipleSelection:self.centerSwitch.on]; 406 | [self.pieChart strokeChart]; 407 | } 408 | 409 | } 410 | 411 | - (IBAction)leftSwitchChanged:(id)sender { 412 | if ([self.title isEqualToString:@"Pie Chart"]) { 413 | UISwitch *showRelative = (UISwitch *) sender; 414 | self.pieChart.showAbsoluteValues = !showRelative.on; 415 | [self.pieChart strokeChart]; 416 | } else if ([self.title isEqualToString:@"Radar Chart"]) { 417 | UISwitch *showRelative = (UISwitch *) sender; 418 | if (showRelative.on) { 419 | self.radarChart.labelStyle = PNRadarChartLabelStyleHorizontal; 420 | } else { 421 | self.radarChart.labelStyle = PNRadarChartLabelStyleCircle; 422 | } 423 | [self.radarChart strokeChart]; 424 | 425 | } else if ([self.title isEqualToString:@"Line Chart"]) { 426 | UISwitch *senderSwitch = (UISwitch *) sender; 427 | if (senderSwitch.isOn) { 428 | UIColor *lineChartLabelColor = [UIColor cyanColor]; 429 | UIColor *darkBackgroundColor = [UIColor colorWithRed:0.47 green:0.47 blue:0.47 alpha:1.0]; 430 | 431 | UIColor *gridLinesForDarkBackgroundColor = [UIColor colorWithRed:242 / 255.0 green:242 / 255.0 blue:242 / 255.0 alpha:1.0]; 432 | self.lineChart.backgroundColor = darkBackgroundColor; 433 | self.lineChart.yGridLinesColor = gridLinesForDarkBackgroundColor; 434 | self.lineChart.showYGridLines = YES; 435 | self.lineChart.yLabelColor = lineChartLabelColor; 436 | self.lineChart.xLabelColor = lineChartLabelColor; 437 | [self.lineChart.chartData enumerateObjectsUsingBlock:^(PNLineChartData *obj, NSUInteger idx, BOOL *stop) { 438 | obj.pointLabelColor = lineChartLabelColor; 439 | }]; 440 | } else { 441 | self.lineChart.backgroundColor = [UIColor whiteColor]; 442 | self.lineChart.yGridLinesColor = [UIColor grayColor]; 443 | self.lineChart.yLabelColor = [UIColor blackColor]; 444 | self.lineChart.xLabelColor = [UIColor blackColor]; 445 | [self.lineChart.chartData enumerateObjectsUsingBlock:^(PNLineChartData *obj, NSUInteger idx, BOOL *stop) { 446 | obj.pointLabelColor = [UIColor blackColor]; 447 | }]; 448 | } 449 | [self.lineChart setXLabels:@[@"DEC 1", @"DEC 2", @"DEC 3", @"DEC 4", @"DEC 5", @"DEC 6", @"DEC 7"]]; 450 | [self.lineChart setYLabels:@[ 451 | @"0 min", 452 | @"50 min", 453 | @"100 min", 454 | @"150 min", 455 | @"200 min", 456 | @"250 min", 457 | @"300 min", 458 | ] 459 | ]; 460 | [self.lineChart strokeChart]; 461 | } 462 | } 463 | 464 | - (IBAction)animationsSwitchChanged:(UISwitch *)sender { 465 | if ([self.title isEqualToString:@"Circle Chart"]) { 466 | self.circleChart.displayAnimated = sender.on; 467 | [self.circleChart strokeChart]; 468 | } else if ([self.title isEqualToString:@"Line Chart"]) { 469 | self.lineChart.displayAnimated = sender.on; 470 | [self.lineChart strokeChart]; 471 | } else if ([self.title isEqualToString:@"Bar Chart"]) { 472 | self.barChart.displayAnimated = sender.on; 473 | [self.barChart strokeChart]; 474 | } else if ([self.title isEqualToString:@"Pie Chart"]) { 475 | self.pieChart.displayAnimated = sender.on; 476 | [self.pieChart strokeChart]; 477 | } else if ([self.title isEqualToString:@"Radar Chart"]) { 478 | self.radarChart.displayAnimated = sender.on; 479 | [self.radarChart strokeChart]; 480 | } 481 | } 482 | 483 | @end 484 | --------------------------------------------------------------------------------