├── .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 | ![iPhone Screenshot1](https://github.com/johnnywjy/JYRadarChart/blob/master/screenshots/screenshot_1.png?raw=true) 10 | ![iPhone Screenshot2](https://github.com/johnnywjy/JYRadarChart/blob/master/screenshots/screenshot_2.png?raw=true) 11 | ![iPhone Screenshot3](https://github.com/johnnywjy/JYRadarChart/blob/master/screenshots/screenshot_3.png?raw=true) 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..