├── 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 | [](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)
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 | [](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)
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)
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)
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)
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 |
--------------------------------------------------------------------------------