├── .swift-version
├── .travis.yml
├── JYRadarChartDemo
├── en.lproj
│ └── InfoPlist.strings
├── ViewController.h
├── AppDelegate.h
├── main.m
├── JYRadarChartDemo-Prefix.pch
├── Images.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── LaunchImage.launchimage
│ │ └── Contents.json
├── Base.lproj
│ ├── Main_iPhone.storyboard
│ └── Main_iPad.storyboard
├── JYRadarChartDemo-Info.plist
├── AppDelegate.m
└── ViewController.m
├── screenshots
├── screenshot_1.png
├── screenshot_2.png
└── screenshot_3.png
├── JYRadarChartDemo.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── JYRadarChartSwiftDemo
├── JYRadarChartSwiftDemo.xcodeproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── project.pbxproj
└── JYRadarChartSwiftDemo
│ ├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Info.plist
│ ├── Base.lproj
│ ├── Main.storyboard
│ └── LaunchScreen.storyboard
│ ├── AppDelegate.swift
│ ├── ViewController.swift
│ ├── JYLegendView.swift
│ └── JYRadarChart.swift
├── .gitignore
├── JYRadarChart.podspec
├── JYLegendView.h
├── LICENSE
├── JYRadarChart.h
├── JYLegendView.m
├── README.md
└── JYRadarChart.m
/.swift-version:
--------------------------------------------------------------------------------
1 | 3.0
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 |
--------------------------------------------------------------------------------
/JYRadarChartDemo/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/screenshots/screenshot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnnywjy/JYRadarChart/HEAD/screenshots/screenshot_1.png
--------------------------------------------------------------------------------
/screenshots/screenshot_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnnywjy/JYRadarChart/HEAD/screenshots/screenshot_2.png
--------------------------------------------------------------------------------
/screenshots/screenshot_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnnywjy/JYRadarChart/HEAD/screenshots/screenshot_3.png
--------------------------------------------------------------------------------
/JYRadarChartDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/JYRadarChartSwiftDemo/JYRadarChartSwiftDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/JYRadarChartDemo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // JYRadarChartDemo
4 | //
5 | // Created by jy on 13-10-31.
6 | // Copyright (c) 2013年 wcode. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | .DS_Store
3 | */build/*
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | profile
14 | *.moved-aside
15 | DerivedData
16 | .idea/
17 | *.hmap
18 | *.xccheckout
19 |
20 | #CocoaPods
21 | Pods
22 |
--------------------------------------------------------------------------------
/JYRadarChartDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // JYRadarChartDemo
4 | //
5 | // Created by jy on 13-10-31.
6 | // Copyright (c) 2013年 wcode. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/JYRadarChartDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // JYRadarChartDemo
4 | //
5 | // Created by jy on 13-10-31.
6 | // Copyright (c) 2013年 wcode. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "AppDelegate.h"
12 |
13 | int main(int argc, char * argv[])
14 | {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/JYRadarChartDemo/JYRadarChartDemo-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 |
--------------------------------------------------------------------------------
/JYRadarChart.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "JYRadarChart"
3 | s.version = "0.4.0"
4 | s.summary = "an iOS open source Radar Chart implementation"
5 | s.homepage = "https://github.com/johnnywjy/JYRadarChart"
6 | s.license = { :type => 'MIT', :file => 'LICENSE' }
7 | s.author = { "Johnny Wu" => "johnny.wjy07@gmail.com" }
8 | s.platform = :ios, '7.0'
9 | s.source = { :git => "https://github.com/johnnywjy/JYRadarChart.git", :tag => s.version.to_s }
10 | s.source_files = '*.{h,m}'
11 | s.framework = 'CoreGraphics'
12 | s.requires_arc = true
13 | end
14 |
--------------------------------------------------------------------------------
/JYLegendView.h:
--------------------------------------------------------------------------------
1 | //
2 | // JYLegendView.h
3 | // JYRadarChart
4 | //
5 | // Created by jy on 13-10-31.
6 | // Copyright (c) 2013年 wcode. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface JYLegendView : UIView
12 |
13 | @property (nonatomic, copy) NSArray *titles;
14 | @property (nonatomic, strong) NSMutableArray *colors;
15 |
16 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
17 | #define JY_TEXT_SIZE(text, font) [text length] > 0 ? [text sizeWithAttributes : @{ NSFontAttributeName : font }] : CGSizeZero;
18 | #define JY_DRAW_TEXT_AT_POINT(text, point, font) [text drawAtPoint : point withAttributes : @{ NSFontAttributeName:font }];
19 | #define JY_DRAW_TEXT_IN_RECT(text, rect, font) [text drawInRect : rect withAttributes : @{ NSFontAttributeName:font }];
20 | #else
21 | #define JY_TEXT_SIZE(text, font) [text length] > 0 ? [text sizeWithFont : font] : CGSizeZero;
22 | #define JY_DRAW_TEXT_AT_POINT(text, point, font) [text drawAtPoint : point withFont : font];
23 | #define JY_DRAW_TEXT_IN_RECT(text, rect, font) [text drawInRect : rect withFont : font];
24 |
25 | #endif
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/JYRadarChartDemo/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "40x40",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "60x60",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "ipad",
20 | "size" : "29x29",
21 | "scale" : "1x"
22 | },
23 | {
24 | "idiom" : "ipad",
25 | "size" : "29x29",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "ipad",
30 | "size" : "40x40",
31 | "scale" : "1x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "40x40",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "76x76",
41 | "scale" : "1x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "76x76",
46 | "scale" : "2x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Johnny Wu
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 |
--------------------------------------------------------------------------------
/JYRadarChartDemo/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 | "orientation" : "portrait",
20 | "idiom" : "ipad",
21 | "extent" : "full-screen",
22 | "minimum-system-version" : "7.0",
23 | "scale" : "1x"
24 | },
25 | {
26 | "orientation" : "landscape",
27 | "idiom" : "ipad",
28 | "extent" : "full-screen",
29 | "minimum-system-version" : "7.0",
30 | "scale" : "1x"
31 | },
32 | {
33 | "orientation" : "portrait",
34 | "idiom" : "ipad",
35 | "extent" : "full-screen",
36 | "minimum-system-version" : "7.0",
37 | "scale" : "2x"
38 | },
39 | {
40 | "orientation" : "landscape",
41 | "idiom" : "ipad",
42 | "extent" : "full-screen",
43 | "minimum-system-version" : "7.0",
44 | "scale" : "2x"
45 | }
46 | ],
47 | "info" : {
48 | "version" : 1,
49 | "author" : "xcode"
50 | }
51 | }
--------------------------------------------------------------------------------
/JYRadarChartSwiftDemo/JYRadarChartSwiftDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/JYRadarChart.h:
--------------------------------------------------------------------------------
1 | //
2 | // JYRadarChart.h
3 | // JYRadarChart
4 | //
5 | // Created by jy on 13-10-31.
6 | // Copyright (c) 2013年 wcode. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface JYRadarChart : UIView
12 |
13 | @property (nonatomic, assign) CGFloat r;
14 | @property (nonatomic, assign) CGFloat maxValue;
15 | @property (nonatomic, assign) CGFloat minValue;
16 | @property (nonatomic, assign) BOOL drawPoints;
17 | @property (nonatomic, assign) CGFloat pointsDiameter;
18 | @property (nonatomic, assign) CGFloat pointsStrokeSize;
19 | @property (nonatomic, assign) CGFloat pointsColorOpacity;
20 | @property (nonatomic, assign) BOOL drawStrokePoints;
21 | @property (nonatomic, assign) BOOL fillArea;
22 | @property (nonatomic, assign) BOOL showLegend;
23 | @property (nonatomic, assign) BOOL showAxes;
24 | @property (nonatomic, assign) BOOL showStepText;
25 | @property (nonatomic, assign) CGFloat colorOpacity;
26 | @property (nonatomic, strong) UIColor *backgroundLineColorRadial;
27 | @property (nonatomic, strong) NSArray *dataSeries;
28 | @property (nonatomic, strong) NSArray *attributes;
29 | @property (nonatomic, assign) NSUInteger steps;
30 | @property (nonatomic, assign) CGPoint centerPoint;
31 | @property (nonatomic, strong) UIColor *backgroundFillColor;
32 |
33 | @property (nonatomic, strong) NSMutableArray *pointsColors;
34 |
35 | @property (nonatomic, assign) BOOL clockwise; //direction of data
36 |
37 | - (void)setTitles:(NSArray *)titles;
38 | - (void)setColors:(NSArray *)colors;
39 | - (void)setPointsColors:(NSMutableArray *)pointsColors;
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/JYRadarChartSwiftDemo/JYRadarChartSwiftDemo/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 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/JYRadarChartDemo/Base.lproj/Main_iPhone.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 |
26 |
--------------------------------------------------------------------------------
/JYRadarChartDemo/Base.lproj/Main_iPad.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 |
26 |
--------------------------------------------------------------------------------
/JYRadarChartDemo/JYRadarChartDemo-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIdentifier
12 | me.wcode.${PRODUCT_NAME:rfc1034identifier}
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 | UIMainStoryboardFile
28 | Main_iPhone
29 | UIMainStoryboardFile~ipad
30 | Main_iPad
31 | UIRequiredDeviceCapabilities
32 |
33 | armv7
34 |
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/JYRadarChartSwiftDemo/JYRadarChartSwiftDemo/Base.lproj/Main.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 |
26 |
27 |
--------------------------------------------------------------------------------
/JYRadarChartSwiftDemo/JYRadarChartSwiftDemo/Base.lproj/LaunchScreen.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 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/JYRadarChartDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // JYRadarChartDemo
4 | //
5 | // Created by jy on 13-10-31.
6 | // Copyright (c) 2013年 wcode. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @implementation AppDelegate
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 |
--------------------------------------------------------------------------------
/JYRadarChartSwiftDemo/JYRadarChartSwiftDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // JYRadarChartSwiftDemo
4 | //
5 | // Created by Erick Santos on 30/04/17.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // 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.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // 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.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // 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.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/JYLegendView.m:
--------------------------------------------------------------------------------
1 | //
2 | // JYLegendView.m
3 | // JYRadarChart
4 | //
5 | // Created by jy on 13-10-31.
6 | // Copyright (c) 2013年 wcode. All rights reserved.
7 | //
8 |
9 | #import "JYLegendView.h"
10 |
11 | #define COLOR_PADDING 15
12 | #define PADDING 3
13 | #define FONT_SIZE 10
14 | #define LEGEND_ROUND_RADIUS 7
15 | #define CIRCLE_DIAMETER 6
16 |
17 | @interface JYLegendView ()
18 |
19 | @property (nonatomic, strong) UIFont *legendFont;
20 |
21 | @end
22 |
23 | @implementation JYLegendView
24 |
25 | void CGContextAddRoundedRect(CGContextRef c, CGRect rect, CGFloat radius) {
26 | if (2 * radius > rect.size.height) radius = rect.size.height / 2.0;
27 | if (2 * radius > rect.size.width) radius = rect.size.width / 2.0;
28 | CGContextAddArc(c, rect.origin.x + radius, rect.origin.y + radius, radius, M_PI, M_PI * 1.5, 0);
29 | CGContextAddArc(c, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, radius, M_PI * 1.5, M_PI * 2, 0);
30 | CGContextAddArc(c, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius, radius, M_PI * 2, M_PI * 0.5, 0);
31 | CGContextAddArc(c, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, M_PI * 0.5, M_PI, 0);
32 | CGContextAddLineToPoint(c, rect.origin.x, rect.origin.y + radius);
33 | }
34 |
35 | void CGContextFillRoundedRect(CGContextRef c, CGRect rect, CGFloat radius) {
36 | CGContextBeginPath(c);
37 | CGContextAddRoundedRect(c, rect, radius);
38 | CGContextFillPath(c);
39 | }
40 |
41 | - (id)initWithFrame:(CGRect)frame {
42 | self = [super initWithFrame:frame];
43 | if (self) {
44 | [self setDefaultValues];
45 | }
46 | return self;
47 | }
48 |
49 | - (id)initWithCoder:(NSCoder *)aDecoder {
50 | self = [super initWithCoder:aDecoder];
51 | if (self) {
52 | [self setDefaultValues];
53 | }
54 | return self;
55 | }
56 |
57 | - (void)setDefaultValues {
58 | _legendFont = [UIFont systemFontOfSize:FONT_SIZE];
59 | }
60 |
61 | - (void)drawRect:(CGRect)rect {
62 | CGContextRef c = UIGraphicsGetCurrentContext();
63 | CGContextSetFillColorWithColor(c, [[UIColor colorWithWhite:0.0 alpha:0.1] CGColor]);
64 | CGContextFillRoundedRect(c, self.bounds, LEGEND_ROUND_RADIUS);
65 |
66 | CGFloat y = 0;
67 | for (int i = 0; i < self.titles.count; i++) {
68 | NSString *title = self.titles[i];
69 | UIColor *color = self.colors[i];
70 | if (color) {
71 | [color setFill];
72 | CGContextFillEllipseInRect(c, CGRectMake(PADDING + 2,
73 | PADDING + round(y) + self.legendFont.xHeight / 2 + 1,
74 | CIRCLE_DIAMETER, CIRCLE_DIAMETER));
75 | }
76 | [[UIColor blackColor] set];
77 | JY_DRAW_TEXT_AT_POINT(title, CGPointMake(COLOR_PADDING + PADDING, y + PADDING), self.legendFont);
78 | y += [self.legendFont lineHeight];
79 | }
80 | }
81 |
82 | - (CGSize)sizeThatFits:(CGSize)size {
83 | CGFloat h = [self.legendFont lineHeight] * [self.titles count];
84 | CGFloat w = 0;
85 | for (NSString *title in self.titles) {
86 | CGSize s = JY_TEXT_SIZE(title, self.legendFont);
87 | w = MAX(w, s.width);
88 | }
89 | return CGSizeMake(COLOR_PADDING + w + 2 * PADDING, h + 2 * PADDING);
90 | }
91 |
92 | @end
93 |
--------------------------------------------------------------------------------
/JYRadarChartSwiftDemo/JYRadarChartSwiftDemo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // JYRadarChartSwiftDemo
4 | //
5 | // Created by Erick Santos on 30/04/17.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | let p = JYRadarChart(frame: CGRect(x: 30.0, y: 20.0, width: 200.0, height: 200.0))
14 |
15 |
16 | override func viewDidLoad() {
17 | super.viewDidLoad()
18 |
19 | let a1: Array = [81.0, 97.0, 87.0, 60.0, 65.0, 77.0]
20 | let a2: Array = [91.0, 87.0, 33.0, 77.0, 78.0, 96.0]
21 | p.data = [a1, a2]
22 | p.steps = 1
23 | p.isShowStepText = true
24 | p.backgroundColor = UIColor.white
25 | p.r = 60
26 | p.minValue = 20
27 | p.maxValue = 120
28 | p.isFillArea = true
29 | p.colorOpacity = 0.7
30 | p.backgroundFillColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1.0)
31 | p.attributes = ["Attack", "Defense", "Speed", "HP", "MP", "IQ"]
32 | p.isShowLegend = true
33 | p.titles = ["archer", "footman"]
34 | p.colors = [UIColor.yellow, UIColor.purple];
35 | p.isShowTotal = false
36 | self.view.addSubview(p)
37 |
38 | Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(updateData), userInfo: nil, repeats: true)
39 |
40 |
41 | let p2 = JYRadarChart(frame: CGRect(x: 10.0, y: 220.0, width: 280.0, height: 200.0))
42 | p2.centerPoint = CGPoint(x: 130, y: 100)
43 | p2.isShowLegend = true
44 | p2.backgroundFillColor = UIColor.white
45 | p2.titles = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
46 | p2.isDrawPoints = true
47 | p2.attributes = ["Price", "Value", "Pressure", "Height", "Weight", "Grade", "Volume", "Length", "Size", "Padding", "Pages", "HAHAHA"]
48 | let b1: Array = [61.0, 97.0, 87.0, 60.0, 85.0, 77.0, 73.0, 74.0, 53.0, 82.0, 65.0, 61.0]
49 | let b2: Array = [91.0, 87.0, 43.0, 77.0, 78.0, 96.0, 51.0, 65.0, 77.0, 55.0, 84.0, 91.0]
50 | let b3: Array = [51.0, 97.0, 87.0, 60.0, 25.0, 77.0, 93.0, 14.0, 53.0, 34.0, 65.0, 51.0]
51 | let b4: Array = [11.0, 87.0, 65.0, 77.0, 55.0, 84.0, 43.0, 77.0, 78.0, 96.0, 51.0, 11.0]
52 | let b5: Array = [41.0, 97.0, 87.0, 60.0, 95.0, 77.0, 73.0, 74.0, 59.0, 82.0, 95.0, 41.0]
53 | let b6: Array = [61.0, 96.0, 51.0, 65.0, 77.0, 87.0, 43.0, 70.0, 78.0, 55.0, 44.0, 61.0]
54 | let b7: Array = [81.0, 97.0, 74.0, 53.0, 82.0, 65.0, 87.0, 60.0, 85.0, 77.0, 73.0, 81.0]
55 | let b8: Array = [91.0, 84.0, 43.0, 67.0, 78.0, 96.0, 47.0, 55.0, 67.0, 55.0, 51.0, 91.0]
56 | let b9: Array = [38.0, 85.0, 77.0, 93.0, 74.0, 53.0, 82.0, 97.0, 87.0, 60.0, 65.0, 38.0]
57 | let b10: Array = [31.0, 87.0, 43.0, 37.0, 78.0, 96.0, 51.0, 65.0, 17.0, 55.0, 54.0, 31.0]
58 | p2.data = [b1, b2, b3, b4, b5, b6, b7, b8, b9, b10]
59 | p2.steps = 2;
60 | p2.isShowTotal = false
61 | p2.backgroundColor = UIColor.gray
62 | self.view.addSubview(p2)
63 | }
64 |
65 | func updateData() {
66 | let n = 6
67 | var a = Array()
68 | var b = Array()
69 | var c = Array()
70 |
71 |
72 | for _ in 0.. rect.size.height) {
28 | radiusAux = rect.size.height / 2.0
29 | }
30 |
31 | if (2 * radiusAux > rect.size.width) {
32 | radiusAux = rect.size.width / 2.0
33 | }
34 |
35 | c.addArc(center: CGPoint(x: rect.origin.x + radiusAux,
36 | y: rect.origin.y + radiusAux),
37 | radius: radiusAux,
38 | startAngle: CGFloat(Double.pi),
39 | endAngle: CGFloat(Double.pi) * 1.5,
40 | clockwise: false)
41 |
42 | c.addArc(center: CGPoint(x: rect.origin.x + rect.size.width - radiusAux,
43 | y: rect.origin.y + radiusAux),
44 | radius: radiusAux,
45 | startAngle: CGFloat(Double.pi) * 1.5,
46 | endAngle: CGFloat(Double.pi) * 2.0,
47 | clockwise: false)
48 |
49 | c.addArc(center: CGPoint(x: rect.origin.x + rect.size.width - radiusAux,
50 | y: rect.origin.y + rect.size.height - radiusAux),
51 | radius: radiusAux,
52 | startAngle: CGFloat(Double.pi) * 2.0,
53 | endAngle: CGFloat(Double.pi) * 0.5,
54 | clockwise: false)
55 |
56 | c.addArc(center: CGPoint(x: rect.origin.x + radiusAux,
57 | y: rect.origin.y + rect.size.height - radiusAux),
58 | radius: radiusAux,
59 | startAngle: CGFloat(Double.pi) * 0.5,
60 | endAngle: CGFloat(Double.pi),
61 | clockwise: false)
62 |
63 | c.addLine(to: CGPoint(x: rect.origin.x, y: rect.origin.y + radiusAux))
64 |
65 | }
66 |
67 | func GContextFillRoundedRect(c: CGContext, rect: CGRect, radius: CGFloat) {
68 | c.beginPath()
69 |
70 | let clipPath: CGPath = UIBezierPath(roundedRect: rect, cornerRadius: radius).cgPath
71 |
72 | c.addPath(clipPath)
73 | c.fillPath()
74 | }
75 |
76 | override func draw(_ rect: CGRect) {
77 |
78 | let context: CGContext? = UIGraphicsGetCurrentContext()
79 | context?.setFillColor(UIColor.init(white: 0.0, alpha: 0.1).cgColor)
80 |
81 | let clipPath: CGPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: CGFloat(roundRadius)).cgPath
82 |
83 | context?.addPath(clipPath)
84 |
85 | var y: CGFloat = 0
86 |
87 | for (index, title) in titles.enumerated() {
88 |
89 | var color = UIColor.black
90 |
91 | if colors.count == titles.count {
92 | color = colors[index]
93 | }
94 |
95 | color.setFill()
96 | context?.fillEllipse(in: CGRect(x: padding + 2.0,
97 | y: padding + round(y) + legendFont.xHeight / 2 + 1,
98 | width: circleDiameter,
99 | height: circleDiameter))
100 |
101 |
102 | UIColor.black.set()
103 |
104 | title.draw(at: CGPoint(x: colorPadding + padding, y: y + padding), withAttributes: [NSFontAttributeName: legendFont])
105 |
106 | y = y + legendFont.lineHeight
107 | }
108 |
109 | }
110 |
111 | override func sizeThatFits(_ size: CGSize) -> CGSize {
112 | let height: CGFloat = legendFont.lineHeight * CGFloat(titles.count)
113 | var width: CGFloat = 0
114 |
115 | titles.forEach {
116 | let size: CGSize = $0.size(attributes: [NSFontAttributeName: legendFont])
117 | width = max(width, size.width)
118 | }
119 |
120 | return CGSize(width: colorPadding + width + 2 * padding, height: height + 2 * padding)
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JYRadarChart
2 |
3 |
4 | an open source iOS Radar Chart implementation
5 |
6 |
7 | ##Screenshots
8 |
9 | 
10 | 
11 | 
12 |
13 |
14 | ## Requirements
15 | * Xcode 5 or higher
16 | * iOS 5.0 or higher
17 | * ARC
18 | * CoreGraphics.framework
19 |
20 | ## Demo
21 |
22 | Build and run the `JYRadarChartDemo` project in Xcode
23 |
24 |
25 | ##Installation
26 |
27 | ###CocoaPods
28 |
29 |
30 | 1. Create a Podfile in your project folder if you don't have one
31 | 2. Add a pod entry for JYRadarChart to your Podfile `pod 'JYRadarChart'`
32 | 3. Install the pod(s) by running `pod install`
33 | 4. Include JYRadarChart wherever you need it with `#import "JYRadarChart.h"`
34 |
35 | ###Manual Install
36 |
37 | All you need to do is drop JYRadarChart files into your project, and add `#import "JYRadarChart.h"` to the top of classes that will use it.
38 |
39 |
40 | ##Example Usage
41 |
42 | ###minimum
43 | JYRadarChart *p = [[JYRadarChart alloc] initWithFrame:CGRectMake(30, 20, 200, 200)];
44 | p.dataSeries = @[@[@(51),@(44),@(94),@(84),@(90)]];
45 | p.attributes = @[@"attack",@"defense",@"speed",@"HP",@"MP"];
46 | [self.view addSubview:p];
47 |
48 |
49 | ###fully customized
50 |
51 | JYRadarChart *p = [[JYRadarChart alloc] initWithFrame:CGRectMake(20, 20, 200, 200)];
52 |
53 | NSArray *a1 = @[@(81), @(97), @(87), @(60), @(65), @(77)];
54 | NSArray *a2 = @[@(91), @(87), @(33), @(77), @(78), @(96)];
55 |
56 | //set the data series
57 | p.dataSeries = @[a1, a2];
58 |
59 | //how many "circles" in the chart
60 | p.steps = 4;
61 |
62 | //for the the entire background
63 | p.backgroundColor = [UIColor whiteColor];
64 |
65 | //you can specify the background fill color
66 | //(just for the chart, not the entire background of the view)
67 | p.backgroundFillColor = [UIColor grayColor];
68 |
69 | //you can set radius, min and max value by yourself, but if you
70 | //leave r (will fill the rect), minValue (will be 0), maxValue (default is 100) alone,
71 | //that is okay. the points with too big value will be out of the chart and thus invisible
72 | p.r = 60;
73 | p.minValue = 20;
74 | p.maxValue = 120;
75 |
76 | //you can choose whether fill area or not (just draw lines)
77 | p.fillArea = YES;
78 |
79 | //you can specify the opacity, default is 1.0 (opaque)
80 | p.colorOpacity = 0.7;
81 | p.attributes = @[@"Attack", @"Defense", @"Speed", @"HP", @"MP", @"IQ"];
82 |
83 | //if you do not need a legend, you can safely get rid of setTitles:
84 | p.showLegend = YES;
85 | [p setTitles:@[@"archer", @"footman"]];
86 |
87 | //there is a color generator in the code, it will generate colors for you
88 | //so if you do not want to specify the colors yourself, just delete the line below
89 | [p setColors:@[[UIColor grayColor],[UIColor blackColor]]];
90 | [self.view addSubview:p];
91 |
92 |
93 |
94 | ##Customization
95 |
96 | here are all the properties you can change, you can find them in `JYRadarChart.h`
97 |
98 | ```
99 | @property (nonatomic, assign) CGFloat r;
100 | @property (nonatomic, assign) CGFloat maxValue;
101 | @property (nonatomic, assign) CGFloat minValue;
102 | @property (nonatomic, assign) BOOL drawPoints;
103 | @property (nonatomic, assign) BOOL fillArea;
104 | @property (nonatomic, assign) BOOL showLegend;
105 | @property (nonatomic, assign) BOOL showStepText;
106 | @property (nonatomic, assign) CGFloat colorOpacity;
107 | @property (nonatomic, strong) UIColor *backgroundLineColorRadial;
108 | @property (nonatomic, strong) UIColor *backgroundFillColor;
109 | @property (nonatomic, strong) NSArray *dataSeries;
110 | @property (nonatomic, strong) NSArray *attributes;
111 | @property (nonatomic, assign) NSUInteger steps;
112 | @property (nonatomic, assign) CGPoint centerPoint;
113 |
114 | - (void)setTitles:(NSArray *)titles;
115 | - (void)setColors:(NSArray *)colors;
116 | ```
117 |
118 |
119 | ##Contact
120 |
121 | johnny.wjy07#gmail.com
122 |
123 | Feel free to send me pull requests!
124 |
125 | Enjoy it~
126 |
127 |
128 | ##License
129 |
130 | JYRadarChart is available under the MIT license.
131 |
132 | The MIT License (MIT)
133 |
134 | Copyright (c) 2013 Johnny Wu
135 |
136 | Permission is hereby granted, free of charge, to any person obtaining a copy of
137 | this software and associated documentation files (the "Software"), to deal in
138 | the Software without restriction, including without limitation the rights to
139 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
140 | the Software, and to permit persons to whom the Software is furnished to do so,
141 | subject to the following conditions:
142 |
143 | The above copyright notice and this permission notice shall be included in all
144 | copies or substantial portions of the Software.
145 |
146 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
147 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
148 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
149 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
150 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
151 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/JYRadarChart.m:
--------------------------------------------------------------------------------
1 | //
2 | // JYRadarChart.m
3 | // JYRadarChart
4 | //
5 | // Created by jy on 13-10-31.
6 | // Copyright (c) 2013年 wcode. All rights reserved.
7 | //
8 |
9 | #import "JYRadarChart.h"
10 | #import "JYLegendView.h"
11 |
12 | #define PADDING 13
13 | #define LEGEND_PADDING 3
14 | #define ATTRIBUTE_TEXT_SIZE 10
15 | #define COLOR_HUE_STEP 5
16 | #define MAX_NUM_OF_COLOR 17
17 |
18 | @interface JYRadarChart ()
19 |
20 | @property (nonatomic, assign) NSUInteger numOfV;
21 | @property (nonatomic, strong) JYLegendView *legendView;
22 | @property (nonatomic, strong) UIFont *scaleFont;
23 |
24 | @end
25 |
26 | @implementation JYRadarChart
27 |
28 | - (id)initWithFrame:(CGRect)frame {
29 | self = [super initWithFrame:frame];
30 | if (self) {
31 | [self setDefaultValues];
32 | }
33 | return self;
34 | }
35 |
36 | - (id)initWithCoder:(NSCoder *)aDecoder {
37 | self = [super initWithCoder:aDecoder];
38 | if (self) {
39 | [self setDefaultValues];
40 | }
41 | return self;
42 | }
43 |
44 | - (void)setDefaultValues {
45 | self.backgroundColor = [UIColor whiteColor];
46 | _maxValue = 100.0;
47 | _centerPoint = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2);
48 | _r = MIN(self.frame.size.width / 2 - PADDING, self.frame.size.height / 2 - PADDING);
49 | _steps = 1;
50 | _drawPoints = NO;
51 | _pointsDiameter = 8;
52 | _pointsStrokeSize = 2;
53 | _pointsColorOpacity = 1;
54 | _drawStrokePoints = YES;
55 | _showLegend = NO;
56 | _showAxes = YES;
57 | _showStepText = NO;
58 | _fillArea = NO;
59 | _minValue = 0;
60 | _colorOpacity = 1.0;
61 | _backgroundLineColorRadial = [UIColor darkGrayColor];
62 | _backgroundFillColor = [UIColor whiteColor];
63 |
64 | _pointsColors = nil;
65 |
66 | _legendView = [[JYLegendView alloc] init];
67 | _legendView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin;
68 | _legendView.backgroundColor = [UIColor clearColor];
69 | _legendView.colors = [NSMutableArray array];
70 | _attributes = @[@"you", @"should", @"set", @"these", @"data", @"titles,",
71 | @"this", @"is", @"just", @"a", @"placeholder"];
72 |
73 | _scaleFont = [UIFont systemFontOfSize:ATTRIBUTE_TEXT_SIZE];
74 |
75 | _clockwise = YES;
76 | }
77 |
78 | - (void)setShowLegend:(BOOL)showLegend {
79 | _showLegend = showLegend;
80 | if (_showLegend) {
81 | [self addSubview:self.legendView];
82 | }
83 | else {
84 | for (UIView *subView in self.subviews) {
85 | if ([subView isKindOfClass:[JYLegendView class]]) {
86 | [subView removeFromSuperview];
87 | }
88 | }
89 | }
90 | }
91 |
92 | - (void)setTitles:(NSArray *)titles {
93 | self.legendView.titles = titles;
94 | }
95 |
96 | - (void)setColors:(NSArray *)colors {
97 | [self.legendView.colors removeAllObjects];
98 | for (UIColor *color in colors) {
99 | [self.legendView.colors addObject:[color colorWithAlphaComponent:self.colorOpacity]];
100 | }
101 | }
102 |
103 | - (void)setPointsColors:(NSMutableArray *)pointsColors{
104 | _pointsColors = [NSMutableArray array];
105 | for (UIColor *color in pointsColors) {
106 | [_pointsColors addObject:[color colorWithAlphaComponent:self.pointsColorOpacity]];
107 | }
108 | }
109 |
110 | - (void)setNeedsDisplay {
111 | [super setNeedsDisplay];
112 | [self.legendView sizeToFit];
113 | [self.legendView setNeedsDisplay];
114 | }
115 |
116 | - (void)setDataSeries:(NSMutableArray *)dataSeries {
117 | _dataSeries = dataSeries;
118 | NSArray *arr = _dataSeries[0];
119 | _numOfV = [arr count];
120 | if (self.legendView.colors.count < _dataSeries.count) {
121 | for (int i = 0; i < _dataSeries.count; i++) {
122 | UIColor *color = [UIColor colorWithHue:1.0 * (i * COLOR_HUE_STEP % MAX_NUM_OF_COLOR) / MAX_NUM_OF_COLOR
123 | saturation:1
124 | brightness:1
125 | alpha:self.colorOpacity];
126 | self.legendView.colors[i] = color;
127 | }
128 | }
129 | }
130 |
131 | - (void)layoutSubviews {
132 | [self.legendView sizeToFit];
133 | CGRect r = self.legendView.frame;
134 | r.origin.x = self.frame.size.width - self.legendView.frame.size.width - LEGEND_PADDING;
135 | r.origin.y = LEGEND_PADDING;
136 | self.legendView.frame = r;
137 | [self bringSubviewToFront:self.legendView];
138 | }
139 |
140 | - (void)drawRect:(CGRect)rect {
141 | NSArray *colors = [self.legendView.colors copy];
142 | CGFloat radPerV = M_PI * 2 / _numOfV;
143 |
144 | if (_clockwise) {
145 | radPerV = - (M_PI * 2 / _numOfV);
146 | }
147 | else
148 | {
149 | radPerV = (M_PI * 2 / _numOfV);
150 | }
151 |
152 | CGContextRef context = UIGraphicsGetCurrentContext();
153 |
154 | //draw attribute text
155 | CGFloat height = [self.scaleFont lineHeight];
156 | CGFloat padding = 2.0;
157 | for (int i = 0; i < _numOfV; i++) {
158 | NSString *attributeName = _attributes[i];
159 | CGPoint pointOnEdge = CGPointMake(_centerPoint.x - _r * sin(i * radPerV), _centerPoint.y - _r * cos(i * radPerV));
160 |
161 | CGSize attributeTextSize = JY_TEXT_SIZE(attributeName, self.scaleFont);
162 | NSInteger width = attributeTextSize.width;
163 |
164 | CGFloat xOffset = pointOnEdge.x >= _centerPoint.x ? width / 2.0 + padding : -width / 2.0 - padding;
165 | CGFloat yOffset = pointOnEdge.y >= _centerPoint.y ? height / 2.0 + padding : -height / 2.0 - padding;
166 | CGPoint legendCenter = CGPointMake(pointOnEdge.x + xOffset, pointOnEdge.y + yOffset);
167 |
168 | if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 70000) {
169 | NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
170 | [paragraphStyle setLineBreakMode:NSLineBreakByClipping];
171 | [paragraphStyle setAlignment:NSTextAlignmentCenter];
172 |
173 | NSDictionary *attributes = @{ NSFontAttributeName: self.scaleFont,
174 | NSParagraphStyleAttributeName: paragraphStyle };
175 |
176 | [attributeName drawInRect:CGRectMake(legendCenter.x - width / 2.0,
177 | legendCenter.y - height / 2.0,
178 | width,
179 | height)
180 | withAttributes:attributes];
181 | }
182 | else {
183 | [attributeName drawInRect:CGRectMake(legendCenter.x - width / 2.0,
184 | legendCenter.y - height / 2.0,
185 | width,
186 | height)
187 | withFont:self.scaleFont
188 | lineBreakMode:NSLineBreakByClipping
189 | alignment:NSTextAlignmentCenter];
190 | }
191 | }
192 |
193 | //draw background fill color
194 | [_backgroundFillColor setFill];
195 | CGContextMoveToPoint(context, _centerPoint.x, _centerPoint.y - _r);
196 | for (int i = 1; i <= _numOfV; ++i) {
197 | CGContextAddLineToPoint(context, _centerPoint.x - _r * sin(i * radPerV),
198 | _centerPoint.y - _r * cos(i * radPerV));
199 | }
200 | CGContextFillPath(context);
201 |
202 | //draw steps line
203 | //static CGFloat dashedPattern[] = {3,3};
204 | //TODO: make this color a variable
205 | [[UIColor lightGrayColor] setStroke];
206 | CGContextSaveGState(context);
207 | for (int step = 1; step <= _steps; step++) {
208 | for (int i = 0; i <= _numOfV; ++i) {
209 | if (i == 0) {
210 | CGContextMoveToPoint(context, _centerPoint.x, _centerPoint.y - _r * step / _steps);
211 | }
212 | else {
213 | // CGContextSetLineDash(context, 0, dashedPattern, 2);
214 | CGContextAddLineToPoint(context, _centerPoint.x - _r * sin(i * radPerV) * step / _steps,
215 | _centerPoint.y - _r * cos(i * radPerV) * step / _steps);
216 | }
217 | }
218 | CGContextStrokePath(context);
219 | }
220 | CGContextRestoreGState(context);
221 |
222 | //draw lines from center
223 | if(_showAxes){
224 | [_backgroundLineColorRadial setStroke];
225 | for (int i = 0; i < _numOfV; i++) {
226 | CGContextMoveToPoint(context, _centerPoint.x, _centerPoint.y);
227 | CGContextAddLineToPoint(context,
228 | _centerPoint.x - _r * sin(i * radPerV),
229 | _centerPoint.y - _r * cos(i * radPerV));
230 | CGContextStrokePath(context);
231 | }
232 | }
233 | //end of base except axis label
234 |
235 |
236 | CGContextSetLineWidth(context, 2.0);
237 |
238 | //draw lines
239 | if (_numOfV > 0) {
240 | for (int serie = 0; serie < [_dataSeries count]; serie++) {
241 | if (self.fillArea) {
242 | [colors[serie] setFill];
243 | }
244 | else {
245 | [colors[serie] setStroke];
246 | }
247 | for (int i = 0; i < _numOfV; ++i) {
248 | CGFloat value = [_dataSeries[serie][i] floatValue];
249 | if (i == 0) {
250 | CGContextMoveToPoint(context, _centerPoint.x, _centerPoint.y - (value - _minValue) / (_maxValue - _minValue) * _r);
251 | }
252 | else {
253 | CGContextAddLineToPoint(context, _centerPoint.x - (value - _minValue) / (_maxValue - _minValue) * _r * sin(i * radPerV),
254 | _centerPoint.y - (value - _minValue) / (_maxValue - _minValue) * _r * cos(i * radPerV));
255 | }
256 | }
257 | CGFloat value = [_dataSeries[serie][0] floatValue];
258 | CGContextAddLineToPoint(context, _centerPoint.x, _centerPoint.y - (value - _minValue) / (_maxValue - _minValue) * _r);
259 |
260 | if (self.fillArea) {
261 | CGContextFillPath(context);
262 | }
263 | else {
264 | CGContextStrokePath(context);
265 | }
266 |
267 |
268 | //draw data points
269 | if (_drawPoints) {
270 | for (int i = 0; i < _numOfV; i++) {
271 | CGFloat value = [_dataSeries[serie][i] floatValue];
272 | CGFloat xVal = _centerPoint.x - (value - _minValue) / (_maxValue - _minValue) * _r * sin(i * radPerV);
273 | CGFloat yVal = _centerPoint.y - (value - _minValue) / (_maxValue - _minValue) * _r * cos(i * radPerV);
274 |
275 | [_pointsColors[serie] setFill];
276 | CGContextFillEllipseInRect(context, CGRectMake(xVal - _pointsDiameter/2, yVal - _pointsDiameter/2, _pointsDiameter, _pointsDiameter));
277 | if(_drawStrokePoints){
278 | [self.backgroundColor setFill];
279 | CGContextFillEllipseInRect(context, CGRectMake(xVal - _pointsDiameter/2 + _pointsStrokeSize, yVal - _pointsDiameter/2 + _pointsStrokeSize,
280 | _pointsDiameter-_pointsStrokeSize*2, _pointsDiameter-_pointsStrokeSize*2));
281 | }
282 | }
283 | }
284 | }
285 | }
286 |
287 | if (self.showStepText) {
288 | //draw step label text, alone y axis
289 | //TODO: make this color a variable
290 | [[UIColor blackColor] setFill];
291 | for (int step = 0; step <= _steps; step++) {
292 | CGFloat value = _minValue + (_maxValue - _minValue) * step / _steps;
293 | NSString *currentLabel = [NSString stringWithFormat:@"%.0f", value];
294 | JY_DRAW_TEXT_IN_RECT(currentLabel,
295 | CGRectMake(_centerPoint.x + 3,
296 | _centerPoint.y - _r * step / _steps - 3,
297 | 20,
298 | 10),
299 | self.scaleFont);
300 | }
301 | }
302 | }
303 |
304 | @end
305 |
--------------------------------------------------------------------------------
/JYRadarChartSwiftDemo/JYRadarChartSwiftDemo/JYRadarChart.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JYRadarChart.swift
3 | // JYRadarChartSwiftDemo
4 | //
5 | // Created by Erick Santos on 30/04/17.
6 | //
7 | //
8 |
9 | import UIKit
10 |
11 | class JYRadarChart: UIView {
12 |
13 | private let padding: CGFloat = 13.0
14 | private let legendPadding: CGFloat = 3.0
15 | private let atributeTextSize: CGFloat = 10.0
16 | private let colorHueStep: CGFloat = 5.0
17 | private let maxNumOfColor: CGFloat = 17.0
18 |
19 | lazy var r: CGFloat = {
20 | [unowned self] in
21 | min(self.frame.size.width / 2 - self.padding, self.frame.size.height / 2 - self.padding)
22 | }()
23 | var maxValue: CGFloat = 100.0
24 | var minValue: CGFloat = 0.0
25 | var isDrawPoints: Bool = false
26 | var isFillArea: Bool = false
27 | private var isShowTextLegend: Bool = false
28 | var isShowLegend: Bool {
29 | get {
30 | return self.isShowTextLegend
31 | }
32 | set {
33 | self.isShowTextLegend = newValue
34 | if self.isShowTextLegend {
35 | addSubview(legendView)
36 | }
37 | else {
38 | for subview in subviews {
39 | if subview.isKind(of: JYLegendView.self) {
40 | subview.removeFromSuperview()
41 | }
42 | }
43 | }
44 | }
45 | }
46 | var isShowStepText: Bool = false
47 | var colorOpacity: CGFloat = 1.0
48 | var backgroundLineColorRadial: UIColor = UIColor.black
49 |
50 | private var dataSeries: Array> = []
51 | var data: Array> {
52 | get {
53 | return self.dataSeries
54 | }
55 |
56 | set {
57 | self.dataSeries = newValue
58 | self.numOfV = self.dataSeries[0].count
59 | self.legendView.colors = []
60 |
61 | if self.legendView.colors.count < self.dataSeries.count {
62 | for index in 0.. = []
71 | var steps: Int = 1
72 | lazy var centerPoint: CGPoint = {
73 | [unowned self] in
74 | return CGPoint(x: self.bounds.size.width / 2, y: self.bounds.size.height / 2)
75 | }()
76 | var backgroundFillColor: UIColor = UIColor.white
77 |
78 | var isClockWise: Bool = true
79 |
80 | var isShowTotal: Bool = true
81 | private var totalTitleString: String? = nil
82 | var totalTitle: String {
83 | get {
84 | if let totalTitleString = self.totalTitleString {
85 | return totalTitleString
86 | } else {
87 | return String(describing: Int(self.maxValue))
88 | }
89 | }
90 | set {
91 | self.totalTitleString = newValue
92 | }
93 | }
94 | var titles: Array {
95 | get {
96 | return self.legendView.titles
97 | }
98 |
99 | set {
100 | self.legendView.titles = newValue
101 | }
102 | }
103 |
104 | var colors: Array {
105 | get {
106 | return self.legendView.colors
107 | }
108 |
109 | set {
110 | self.legendView.colors.removeAll()
111 | for color in newValue {
112 | self.legendView.colors.append(color.withAlphaComponent(self.colorOpacity));
113 | }
114 | }
115 | }
116 |
117 | var stepLinesColor = UIColor.lightGray
118 |
119 | private var numOfV: Int = 0
120 | private let legendView: JYLegendView = {
121 | let legendView = JYLegendView()
122 | legendView.autoresizingMask = [.flexibleLeftMargin, .flexibleBottomMargin]
123 | legendView.backgroundColor = UIColor.clear
124 | legendView.colors = []
125 | return legendView
126 | }()
127 |
128 | var scaleFont: UIFont = UIFont.systemFont(ofSize: 10)
129 | var fontColors: Array = []
130 |
131 | override func setNeedsDisplay() {
132 | super.setNeedsDisplay()
133 |
134 | legendView.sizeToFit()
135 | legendView.setNeedsDisplay()
136 | }
137 |
138 | override func layoutSubviews() {
139 | super.layoutSubviews()
140 |
141 | legendView.sizeToFit()
142 | var r: CGRect = legendView.frame
143 | r.origin.x = frame.size.width - legendView.frame.size.width - legendPadding
144 | r.origin.y = legendPadding
145 | legendView.frame = r
146 | bringSubview(toFront: self.legendView)
147 | }
148 |
149 | override func draw(_ rect: CGRect) {
150 | let colors: Array = legendView.colors
151 | var radPerV: CGFloat = CGFloat(Double.pi * 2.0) / CGFloat(numOfV)
152 |
153 | if isClockWise {
154 | radPerV = -(CGFloat(Double.pi * 2.0) / CGFloat(numOfV))
155 | }
156 |
157 | let context: CGContext? = UIGraphicsGetCurrentContext()
158 |
159 | let height: CGFloat = scaleFont.lineHeight
160 | let padding: CGFloat = 2.0
161 |
162 |
163 | for index in 0.. 0 {
263 | for serie in 0..