├── .gitignore ├── .travis.yml ├── LICENSE ├── Podfile ├── README.md ├── doc ├── rage.png ├── screenshot1.png └── screenshot2.png ├── ios-linechart.podspec ├── ios-linechart.xcodeproj └── project.pbxproj └── ios-linechart ├── ChartAppDelegate.h ├── ChartAppDelegate.m ├── ChartViewController.h ├── ChartViewController.m ├── Default-568h@2x.png ├── Default.png ├── Default@2x.png ├── LCInfoView.h ├── LCInfoView.m ├── LCLegendView.h ├── LCLegendView.m ├── LCLineChartView.h ├── LCLineChartView.m ├── LineChart.h ├── en.lproj ├── InfoPlist.strings └── MainStoryboard.storyboard ├── ios-linechart-Info.plist ├── ios-linechart-Prefix.pch └── main.m /.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 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | Podfile.lock 20 | Pods 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | 3 | rvm: 1.9.3 4 | 5 | before_install: 6 | - gem install cocoapods -v '0.32.1' 7 | - pod repo remove master 8 | - pod setup 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ios-linechart 2 | 3 | Copyright (C) 2012 Marcel Ruegenberg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '5.1' 2 | 3 | pod 'uikit-utils', '~> 0.5.1' 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ios-linechart 2 | Interactive linecharts for the simplicity-loving iOS developer. 3 | 4 | You just want some linecharts, and Core Plot makes you think 5 | 6 | ![fuuu](doc/rage.png) 7 | 8 | Search no more! ios-linechart is here! 9 | 10 | ## Features 11 | - Simple API. Lets you draw the first chart by the time you would need to find sample code for Core Plot. 12 | - Interactivity. Lets users look at single datapoints. 13 | - Support non-uniform steps on the x axis 14 | 15 | ## Installation 16 | 17 | ### CocoaPods 18 | The best way to get ios-linechart is to use [CocoaPods](http://cocoapods.org/). 19 | 20 | ### Without CocoaPods 21 | 22 | If, for some reason, you don't want that, copy the following files into your project: 23 | 24 | * `LCLegendView.h`, `LCLegendView.m` 25 | * `LCLineChartView.h`, `LCLineChartView.m` 26 | * `LCInfoView.h`, `LCInfoView.m` 27 | 28 | Additionally, you will need to get some dependencies from the [objc-utils](https://github.com/mruegenberg/objc-utils) project: 29 | 30 | * `UIKit+DrawingHelpers.{h,m}` (found in `UIKit/Drawing/`) 31 | 32 | Just copy these into you project as well. 33 | 34 | ios-linechart uses Core Graphics, so you'll need to add `CoreGraphics.framework` to your project. 35 | 36 | Even if you don't use Cocoapods, it is recommended to use an official release, since the repository may be unstable in between releases. Just check out the newest tagged commit. 37 | 38 | 39 | 40 | ## Usage 41 | 42 | Basic usage can be seen in the demo app contained in the repository. 43 | 44 | First, import the main header: 45 | 46 | ```obj-c 47 | #import "LCLineChartView.h" 48 | ``` 49 | 50 | Alternatively, you can import the abbreviation header, which provides names without the `LC` prefix: 51 | ```obj-c 52 | #import "LineChart.h" 53 | ``` 54 | 55 | Each chart line is contained in a `LCLineChartData` object, which specifies a range of its data on the x axis (`xMin` / `xMax`), a `color`, a `title` (which is displayed in the legend) and an `itemCount`, the number of data points: 56 | 57 | ```obj-c 58 | LCLineChartData *d = [LCLineChartData new]; 59 | d.xMin = 1; 60 | d.xMax = 31; 61 | d.title = @"The title for the legend"; 62 | d.color = [UIColor redColor]; 63 | d.itemCount = 10; 64 | ``` 65 | 66 | Additionally, each `LCLineChartData` object also has a `getData` property. This is simply a block that takes the number of a data point as its argument and returns `LCLineChartDataItem` objects, which wrap the individual data points: 67 | 68 | ```obj-c 69 | NSMutableArray *vals = [NSMutableArray new]; 70 | for(NSUInteger i = 0; i < d.itemCount; ++i) { 71 | [vals addObject:@((rand() / (float)RAND_MAX) * (31 - 1) + 1)]; 72 | } 73 | [vals sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { 74 | return [obj1 compare:obj2]; 75 | }]; 76 | d.getData = ^(NSUInteger item) { 77 | float x = [vals[item] floatValue]; 78 | float y = powf(2, x / 7); 79 | NSString *label1 = [NSString stringWithFormat:@"%d", item]; 80 | NSString *label2 = [NSString stringWithFormat:@"%f", y]; 81 | return [LCLineChartDataItem dataItemWithX:x y:y xLabel:label1 dataLabel:label2]; 82 | }; 83 | ``` 84 | 85 | The `x` and `y` properties of `LCLineChartDataItem` are, obviously, the x and y values of the data point. `xLabel` is the text displayed on the x axis when the data point is selected, `dataLabel` is usually just a textual representation of the y value, and is displayed in a bubble directly next to the data point when the user touches it. 86 | 87 | Note that, to get a coherent chart, the x values for the items should be sorted. This will be the case anyway for most real-world data. 88 | 89 | Finally, everything is packed up into a `LCLineChartView`: 90 | 91 | ```obj-c 92 | LCLineChartView *chartView = [[LCLineChartView alloc] initWithFrame:CGRectMake(20, 700, 500, 300)]; 93 | chartView.yMin = 0; 94 | chartView.yMax = powf(2, 31 / 7) + 0.5; 95 | chartView.ySteps = @[@"0.0", 96 | [NSString stringWithFormat:@"%.02f", chartView.yMax / 2], 97 | [NSString stringWithFormat:@"%.02f", chartView.yMax]]; 98 | chartView.data = @[d]; 99 | 100 | [self.view addSubview:chartView]; 101 | ``` 102 | 103 | The `yMin` and `yMax` properties should be self-evident. `ySteps` is an array of labels which are placed along the y axis. `data` is an array of all `LineChartData` objects to use. `drawsDataPointOrnaments` is a switch that determines whether circles are drawn around the actual data points. 104 | 105 | ## Screenshots 106 | ![Screenshot 1](doc/screenshot1.png) 107 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmruegenberg%2Fios-linechart.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmruegenberg%2Fios-linechart?ref=badge_shield) 108 | 109 | ![Screenshot 2](doc/screenshot2.png) 110 | 111 | ## FAQ 112 | 113 | - *The sample project doesn't compile* 114 | 115 | The sample project, at the moment, needs CocoaPods. Install Cocoapods, then run `pod install` while you are in the ios-linechart directory. 116 | 117 | - *Will the project support bar charts, pie charts, ...?* 118 | 119 | No. 120 | 121 | ## Contact 122 | 123 | ![Travis CI build status](https://api.travis-ci.org/mruegenberg/ios-linechart.png) 124 | 125 | Bug reports and pull requests are welcome! Contact me via e-mail or just by opening an issue on GitHub. 126 | 127 | 128 | ## License 129 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmruegenberg%2Fios-linechart.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmruegenberg%2Fios-linechart?ref=badge_large) -------------------------------------------------------------------------------- /doc/rage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mruegenberg/ios-linechart/a492b9444ab3a45c33c48546451c6f2d073b8f84/doc/rage.png -------------------------------------------------------------------------------- /doc/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mruegenberg/ios-linechart/a492b9444ab3a45c33c48546451c6f2d073b8f84/doc/screenshot1.png -------------------------------------------------------------------------------- /doc/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mruegenberg/ios-linechart/a492b9444ab3a45c33c48546451c6f2d073b8f84/doc/screenshot2.png -------------------------------------------------------------------------------- /ios-linechart.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "ios-linechart" 3 | s.version = "1.3.2" 4 | s.summary = "Interactive line charts / plots for the simplicity-loving iOS developer." 5 | s.homepage = "https://github.com/mruegenberg/ios-linechart" 6 | s.screenshots = "https://raw.github.com/mruegenberg/ios-linechart/master/doc/screenshot1.png", "https://raw.github.com/mruegenberg/ios-linechart/master/doc/screenshot2.png" 7 | 8 | s.license = { :type => 'MIT', :file => 'LICENSE' } 9 | 10 | s.author = { "Marcel Ruegenberg" => "github@dustlab.com" } 11 | 12 | s.source = { :git => "https://github.com/mruegenberg/ios-linechart.git", :tag => s.version } 13 | 14 | s.platform = :ios, '5.1' 15 | 16 | s.source_files = 'ios-linechart/LCLegendView.{h,m}', 'ios-linechart/LCLineChartView.{h,m}', 'ios-linechart/LCInfoView.{h,m}', 'ios-linechart/LineChart.h' 17 | 18 | s.frameworks = 'CoreFoundation', 'UIKit', 'CoreGraphics' 19 | 20 | s.requires_arc = true 21 | 22 | s.dependency 'uikit-utils', '~> 0.5.1' 23 | end 24 | -------------------------------------------------------------------------------- /ios-linechart.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1C04F87D17AC0F5A00F8A543 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C04F87C17AC0F5A00F8A543 /* UIKit.framework */; }; 11 | 1C04F87F17AC0F5A00F8A543 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C04F87E17AC0F5A00F8A543 /* Foundation.framework */; }; 12 | 1C04F88117AC0F5A00F8A543 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C04F88017AC0F5A00F8A543 /* CoreGraphics.framework */; }; 13 | 1C04F88717AC0F5A00F8A543 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1C04F88517AC0F5A00F8A543 /* InfoPlist.strings */; }; 14 | 1C04F88917AC0F5A00F8A543 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C04F88817AC0F5A00F8A543 /* main.m */; }; 15 | 1C04F88D17AC0F5A00F8A543 /* ChartAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C04F88C17AC0F5A00F8A543 /* ChartAppDelegate.m */; }; 16 | 1C04F88F17AC0F5A00F8A543 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 1C04F88E17AC0F5A00F8A543 /* Default.png */; }; 17 | 1C04F89117AC0F5A00F8A543 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1C04F89017AC0F5A00F8A543 /* Default@2x.png */; }; 18 | 1C04F89317AC0F5A00F8A543 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1C04F89217AC0F5A00F8A543 /* Default-568h@2x.png */; }; 19 | 1C04F89617AC0F5A00F8A543 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1C04F89417AC0F5A00F8A543 /* MainStoryboard.storyboard */; }; 20 | 1C04F89917AC0F5A00F8A543 /* ChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C04F89817AC0F5A00F8A543 /* ChartViewController.m */; }; 21 | 1C04F8A317AC0FA500F8A543 /* LCLegendView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C04F8A017AC0FA500F8A543 /* LCLegendView.m */; }; 22 | 1C04F8A417AC0FA500F8A543 /* LCLineChartView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C04F8A217AC0FA500F8A543 /* LCLineChartView.m */; }; 23 | 1C04F8A717AC402600F8A543 /* LCInfoView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1C04F8A617AC402600F8A543 /* LCInfoView.m */; }; 24 | C4098B43BD504D33BFA4645B /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E88FE8D4A4A24E6E99D658EA /* libPods.a */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 1C04F87917AC0F5A00F8A543 /* ios-linechart.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ios-linechart.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 1C04F87C17AC0F5A00F8A543 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 30 | 1C04F87E17AC0F5A00F8A543 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 31 | 1C04F88017AC0F5A00F8A543 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 32 | 1C04F88417AC0F5A00F8A543 /* ios-linechart-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ios-linechart-Info.plist"; sourceTree = ""; }; 33 | 1C04F88617AC0F5A00F8A543 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 34 | 1C04F88817AC0F5A00F8A543 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | 1C04F88A17AC0F5A00F8A543 /* ios-linechart-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ios-linechart-Prefix.pch"; sourceTree = ""; }; 36 | 1C04F88B17AC0F5A00F8A543 /* ChartAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChartAppDelegate.h; sourceTree = ""; }; 37 | 1C04F88C17AC0F5A00F8A543 /* ChartAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChartAppDelegate.m; sourceTree = ""; }; 38 | 1C04F88E17AC0F5A00F8A543 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 39 | 1C04F89017AC0F5A00F8A543 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 40 | 1C04F89217AC0F5A00F8A543 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 41 | 1C04F89517AC0F5A00F8A543 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = ""; }; 42 | 1C04F89717AC0F5A00F8A543 /* ChartViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChartViewController.h; sourceTree = ""; }; 43 | 1C04F89817AC0F5A00F8A543 /* ChartViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChartViewController.m; sourceTree = ""; }; 44 | 1C04F89F17AC0FA500F8A543 /* LCLegendView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCLegendView.h; sourceTree = ""; }; 45 | 1C04F8A017AC0FA500F8A543 /* LCLegendView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCLegendView.m; sourceTree = ""; }; 46 | 1C04F8A117AC0FA500F8A543 /* LCLineChartView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCLineChartView.h; sourceTree = ""; }; 47 | 1C04F8A217AC0FA500F8A543 /* LCLineChartView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCLineChartView.m; sourceTree = ""; }; 48 | 1C04F8A517AC402600F8A543 /* LCInfoView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCInfoView.h; sourceTree = ""; }; 49 | 1C04F8A617AC402600F8A543 /* LCInfoView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCInfoView.m; sourceTree = ""; }; 50 | 1C5999D118166CA90031C805 /* LineChart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LineChart.h; sourceTree = ""; }; 51 | E763E74E5B5048B682038C66 /* Pods.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = Pods/Pods.xcconfig; sourceTree = SOURCE_ROOT; }; 52 | E88FE8D4A4A24E6E99D658EA /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 1C04F87617AC0F5A00F8A543 /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 1C04F87D17AC0F5A00F8A543 /* UIKit.framework in Frameworks */, 61 | 1C04F87F17AC0F5A00F8A543 /* Foundation.framework in Frameworks */, 62 | 1C04F88117AC0F5A00F8A543 /* CoreGraphics.framework in Frameworks */, 63 | C4098B43BD504D33BFA4645B /* libPods.a in Frameworks */, 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | /* End PBXFrameworksBuildPhase section */ 68 | 69 | /* Begin PBXGroup section */ 70 | 1C04F87017AC0F5A00F8A543 = { 71 | isa = PBXGroup; 72 | children = ( 73 | 1C04F88217AC0F5A00F8A543 /* ios-linechart */, 74 | 1C04F87B17AC0F5A00F8A543 /* Frameworks */, 75 | 1C04F87A17AC0F5A00F8A543 /* Products */, 76 | E763E74E5B5048B682038C66 /* Pods.xcconfig */, 77 | ); 78 | sourceTree = ""; 79 | }; 80 | 1C04F87A17AC0F5A00F8A543 /* Products */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 1C04F87917AC0F5A00F8A543 /* ios-linechart.app */, 84 | ); 85 | name = Products; 86 | sourceTree = ""; 87 | }; 88 | 1C04F87B17AC0F5A00F8A543 /* Frameworks */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 1C04F87C17AC0F5A00F8A543 /* UIKit.framework */, 92 | 1C04F87E17AC0F5A00F8A543 /* Foundation.framework */, 93 | 1C04F88017AC0F5A00F8A543 /* CoreGraphics.framework */, 94 | E88FE8D4A4A24E6E99D658EA /* libPods.a */, 95 | ); 96 | name = Frameworks; 97 | sourceTree = ""; 98 | }; 99 | 1C04F88217AC0F5A00F8A543 /* ios-linechart */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 1C04F89F17AC0FA500F8A543 /* LCLegendView.h */, 103 | 1C04F8A017AC0FA500F8A543 /* LCLegendView.m */, 104 | 1C04F8A117AC0FA500F8A543 /* LCLineChartView.h */, 105 | 1C04F8A217AC0FA500F8A543 /* LCLineChartView.m */, 106 | 1C04F8A517AC402600F8A543 /* LCInfoView.h */, 107 | 1C04F8A617AC402600F8A543 /* LCInfoView.m */, 108 | 1C5999D118166CA90031C805 /* LineChart.h */, 109 | 1C04F88B17AC0F5A00F8A543 /* ChartAppDelegate.h */, 110 | 1C04F88C17AC0F5A00F8A543 /* ChartAppDelegate.m */, 111 | 1C04F89417AC0F5A00F8A543 /* MainStoryboard.storyboard */, 112 | 1C04F89717AC0F5A00F8A543 /* ChartViewController.h */, 113 | 1C04F89817AC0F5A00F8A543 /* ChartViewController.m */, 114 | 1C04F88317AC0F5A00F8A543 /* Supporting Files */, 115 | ); 116 | path = "ios-linechart"; 117 | sourceTree = ""; 118 | }; 119 | 1C04F88317AC0F5A00F8A543 /* Supporting Files */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 1C04F88417AC0F5A00F8A543 /* ios-linechart-Info.plist */, 123 | 1C04F88517AC0F5A00F8A543 /* InfoPlist.strings */, 124 | 1C04F88817AC0F5A00F8A543 /* main.m */, 125 | 1C04F88A17AC0F5A00F8A543 /* ios-linechart-Prefix.pch */, 126 | 1C04F88E17AC0F5A00F8A543 /* Default.png */, 127 | 1C04F89017AC0F5A00F8A543 /* Default@2x.png */, 128 | 1C04F89217AC0F5A00F8A543 /* Default-568h@2x.png */, 129 | ); 130 | name = "Supporting Files"; 131 | sourceTree = ""; 132 | }; 133 | /* End PBXGroup section */ 134 | 135 | /* Begin PBXNativeTarget section */ 136 | 1C04F87817AC0F5A00F8A543 /* ios-linechart */ = { 137 | isa = PBXNativeTarget; 138 | buildConfigurationList = 1C04F89C17AC0F5A00F8A543 /* Build configuration list for PBXNativeTarget "ios-linechart" */; 139 | buildPhases = ( 140 | 5609285230ED4CA5BF152DFA /* Check Pods Manifest.lock */, 141 | 1C04F87517AC0F5A00F8A543 /* Sources */, 142 | 1C04F87617AC0F5A00F8A543 /* Frameworks */, 143 | 1C04F87717AC0F5A00F8A543 /* Resources */, 144 | 4D22652716E949DF8A50557A /* Copy Pods Resources */, 145 | ); 146 | buildRules = ( 147 | ); 148 | dependencies = ( 149 | ); 150 | name = "ios-linechart"; 151 | productName = "ios-linechart"; 152 | productReference = 1C04F87917AC0F5A00F8A543 /* ios-linechart.app */; 153 | productType = "com.apple.product-type.application"; 154 | }; 155 | /* End PBXNativeTarget section */ 156 | 157 | /* Begin PBXProject section */ 158 | 1C04F87117AC0F5A00F8A543 /* Project object */ = { 159 | isa = PBXProject; 160 | attributes = { 161 | CLASSPREFIX = Chart; 162 | LastUpgradeCheck = 0460; 163 | ORGANIZATIONNAME = "Marcel Ruegenberg"; 164 | TargetAttributes = { 165 | 1C04F87817AC0F5A00F8A543 = { 166 | DevelopmentTeam = 28AD228865; 167 | }; 168 | }; 169 | }; 170 | buildConfigurationList = 1C04F87417AC0F5A00F8A543 /* Build configuration list for PBXProject "ios-linechart" */; 171 | compatibilityVersion = "Xcode 3.2"; 172 | developmentRegion = English; 173 | hasScannedForEncodings = 0; 174 | knownRegions = ( 175 | en, 176 | ); 177 | mainGroup = 1C04F87017AC0F5A00F8A543; 178 | productRefGroup = 1C04F87A17AC0F5A00F8A543 /* Products */; 179 | projectDirPath = ""; 180 | projectRoot = ""; 181 | targets = ( 182 | 1C04F87817AC0F5A00F8A543 /* ios-linechart */, 183 | ); 184 | }; 185 | /* End PBXProject section */ 186 | 187 | /* Begin PBXResourcesBuildPhase section */ 188 | 1C04F87717AC0F5A00F8A543 /* Resources */ = { 189 | isa = PBXResourcesBuildPhase; 190 | buildActionMask = 2147483647; 191 | files = ( 192 | 1C04F88717AC0F5A00F8A543 /* InfoPlist.strings in Resources */, 193 | 1C04F88F17AC0F5A00F8A543 /* Default.png in Resources */, 194 | 1C04F89117AC0F5A00F8A543 /* Default@2x.png in Resources */, 195 | 1C04F89317AC0F5A00F8A543 /* Default-568h@2x.png in Resources */, 196 | 1C04F89617AC0F5A00F8A543 /* MainStoryboard.storyboard in Resources */, 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | /* End PBXResourcesBuildPhase section */ 201 | 202 | /* Begin PBXShellScriptBuildPhase section */ 203 | 4D22652716E949DF8A50557A /* Copy Pods Resources */ = { 204 | isa = PBXShellScriptBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | ); 208 | inputPaths = ( 209 | ); 210 | name = "Copy Pods Resources"; 211 | outputPaths = ( 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | shellPath = /bin/sh; 215 | shellScript = "\"${SRCROOT}/Pods/Pods-resources.sh\"\n"; 216 | }; 217 | 5609285230ED4CA5BF152DFA /* Check Pods Manifest.lock */ = { 218 | isa = PBXShellScriptBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | ); 222 | inputPaths = ( 223 | ); 224 | name = "Check Pods Manifest.lock"; 225 | outputPaths = ( 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | shellPath = /bin/sh; 229 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 230 | }; 231 | /* End PBXShellScriptBuildPhase section */ 232 | 233 | /* Begin PBXSourcesBuildPhase section */ 234 | 1C04F87517AC0F5A00F8A543 /* Sources */ = { 235 | isa = PBXSourcesBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | 1C04F88917AC0F5A00F8A543 /* main.m in Sources */, 239 | 1C04F88D17AC0F5A00F8A543 /* ChartAppDelegate.m in Sources */, 240 | 1C04F89917AC0F5A00F8A543 /* ChartViewController.m in Sources */, 241 | 1C04F8A317AC0FA500F8A543 /* LCLegendView.m in Sources */, 242 | 1C04F8A417AC0FA500F8A543 /* LCLineChartView.m in Sources */, 243 | 1C04F8A717AC402600F8A543 /* LCInfoView.m in Sources */, 244 | ); 245 | runOnlyForDeploymentPostprocessing = 0; 246 | }; 247 | /* End PBXSourcesBuildPhase section */ 248 | 249 | /* Begin PBXVariantGroup section */ 250 | 1C04F88517AC0F5A00F8A543 /* InfoPlist.strings */ = { 251 | isa = PBXVariantGroup; 252 | children = ( 253 | 1C04F88617AC0F5A00F8A543 /* en */, 254 | ); 255 | name = InfoPlist.strings; 256 | sourceTree = ""; 257 | }; 258 | 1C04F89417AC0F5A00F8A543 /* MainStoryboard.storyboard */ = { 259 | isa = PBXVariantGroup; 260 | children = ( 261 | 1C04F89517AC0F5A00F8A543 /* en */, 262 | ); 263 | name = MainStoryboard.storyboard; 264 | sourceTree = ""; 265 | }; 266 | /* End PBXVariantGroup section */ 267 | 268 | /* Begin XCBuildConfiguration section */ 269 | 1C04F89A17AC0F5A00F8A543 /* Debug */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | ALWAYS_SEARCH_USER_PATHS = NO; 273 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 274 | CLANG_CXX_LIBRARY = "libc++"; 275 | CLANG_ENABLE_OBJC_ARC = YES; 276 | CLANG_WARN_CONSTANT_CONVERSION = YES; 277 | CLANG_WARN_EMPTY_BODY = YES; 278 | CLANG_WARN_ENUM_CONVERSION = YES; 279 | CLANG_WARN_INT_CONVERSION = YES; 280 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 281 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 282 | COPY_PHASE_STRIP = NO; 283 | GCC_C_LANGUAGE_STANDARD = gnu99; 284 | GCC_DYNAMIC_NO_PIC = NO; 285 | GCC_OPTIMIZATION_LEVEL = 0; 286 | GCC_PREPROCESSOR_DEFINITIONS = ( 287 | "DEBUG=1", 288 | "$(inherited)", 289 | ); 290 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 291 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 292 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 293 | GCC_WARN_UNUSED_VARIABLE = YES; 294 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 295 | ONLY_ACTIVE_ARCH = YES; 296 | SDKROOT = iphoneos; 297 | TARGETED_DEVICE_FAMILY = 2; 298 | }; 299 | name = Debug; 300 | }; 301 | 1C04F89B17AC0F5A00F8A543 /* Release */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ALWAYS_SEARCH_USER_PATHS = NO; 305 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 306 | CLANG_CXX_LIBRARY = "libc++"; 307 | CLANG_ENABLE_OBJC_ARC = YES; 308 | CLANG_WARN_CONSTANT_CONVERSION = YES; 309 | CLANG_WARN_EMPTY_BODY = YES; 310 | CLANG_WARN_ENUM_CONVERSION = YES; 311 | CLANG_WARN_INT_CONVERSION = YES; 312 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 313 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 314 | COPY_PHASE_STRIP = YES; 315 | GCC_C_LANGUAGE_STANDARD = gnu99; 316 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 317 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 318 | GCC_WARN_UNUSED_VARIABLE = YES; 319 | IPHONEOS_DEPLOYMENT_TARGET = 6.1; 320 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 321 | SDKROOT = iphoneos; 322 | TARGETED_DEVICE_FAMILY = 2; 323 | VALIDATE_PRODUCT = YES; 324 | }; 325 | name = Release; 326 | }; 327 | 1C04F89D17AC0F5A00F8A543 /* Debug */ = { 328 | isa = XCBuildConfiguration; 329 | baseConfigurationReference = E763E74E5B5048B682038C66 /* Pods.xcconfig */; 330 | buildSettings = { 331 | CODE_SIGN_IDENTITY = "iPhone Developer"; 332 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 333 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 334 | GCC_PREFIX_HEADER = "ios-linechart/ios-linechart-Prefix.pch"; 335 | INFOPLIST_FILE = "ios-linechart/ios-linechart-Info.plist"; 336 | PRODUCT_NAME = "$(TARGET_NAME)"; 337 | PROVISIONING_PROFILE = ""; 338 | WRAPPER_EXTENSION = app; 339 | }; 340 | name = Debug; 341 | }; 342 | 1C04F89E17AC0F5A00F8A543 /* Release */ = { 343 | isa = XCBuildConfiguration; 344 | baseConfigurationReference = E763E74E5B5048B682038C66 /* Pods.xcconfig */; 345 | buildSettings = { 346 | CODE_SIGN_IDENTITY = "iPhone Developer"; 347 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 348 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 349 | GCC_PREFIX_HEADER = "ios-linechart/ios-linechart-Prefix.pch"; 350 | INFOPLIST_FILE = "ios-linechart/ios-linechart-Info.plist"; 351 | PRODUCT_NAME = "$(TARGET_NAME)"; 352 | PROVISIONING_PROFILE = ""; 353 | WRAPPER_EXTENSION = app; 354 | }; 355 | name = Release; 356 | }; 357 | /* End XCBuildConfiguration section */ 358 | 359 | /* Begin XCConfigurationList section */ 360 | 1C04F87417AC0F5A00F8A543 /* Build configuration list for PBXProject "ios-linechart" */ = { 361 | isa = XCConfigurationList; 362 | buildConfigurations = ( 363 | 1C04F89A17AC0F5A00F8A543 /* Debug */, 364 | 1C04F89B17AC0F5A00F8A543 /* Release */, 365 | ); 366 | defaultConfigurationIsVisible = 0; 367 | defaultConfigurationName = Release; 368 | }; 369 | 1C04F89C17AC0F5A00F8A543 /* Build configuration list for PBXNativeTarget "ios-linechart" */ = { 370 | isa = XCConfigurationList; 371 | buildConfigurations = ( 372 | 1C04F89D17AC0F5A00F8A543 /* Debug */, 373 | 1C04F89E17AC0F5A00F8A543 /* Release */, 374 | ); 375 | defaultConfigurationIsVisible = 0; 376 | defaultConfigurationName = Release; 377 | }; 378 | /* End XCConfigurationList section */ 379 | }; 380 | rootObject = 1C04F87117AC0F5A00F8A543 /* Project object */; 381 | } 382 | -------------------------------------------------------------------------------- /ios-linechart/ChartAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAppDelegate.h 3 | // ios-linechart 4 | // 5 | // Created by Marcel Ruegenberg on 02.08.13. 6 | // Copyright (c) 2013 Marcel Ruegenberg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChartAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ios-linechart/ChartAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAppDelegate.m 3 | // ios-linechart 4 | // 5 | // Created by Marcel Ruegenberg on 02.08.13. 6 | // Copyright (c) 2013 Marcel Ruegenberg. All rights reserved. 7 | // 8 | 9 | #import "ChartAppDelegate.h" 10 | 11 | @implementation ChartAppDelegate 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios-linechart/ChartViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ChartViewController.h 3 | // ios-linechart 4 | // 5 | // Created by Marcel Ruegenberg on 02.08.13. 6 | // Copyright (c) 2013 Marcel Ruegenberg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ChartViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios-linechart/ChartViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ChartViewController.m 3 | // ios-linechart 4 | // 5 | // Created by Marcel Ruegenberg on 02.08.13. 6 | // Copyright (c) 2013 Marcel Ruegenberg. All rights reserved. 7 | // 8 | 9 | #import "ChartViewController.h" 10 | #import "LCLineChartView.h" 11 | 12 | @interface ChartViewController () 13 | 14 | @property (strong) NSDateFormatter *formatter; 15 | 16 | @end 17 | 18 | @implementation ChartViewController 19 | 20 | #define SECS_PER_DAY (86400) 21 | 22 | - (void)viewDidLoad 23 | { 24 | [super viewDidLoad]; 25 | 26 | { 27 | self.formatter = [[NSDateFormatter alloc] init]; 28 | [self.formatter setDateFormat:[NSDateFormatter dateFormatFromTemplate:@"yyyyMMMd" options:0 locale:[NSLocale currentLocale]]]; 29 | } 30 | 31 | // first sample chart view: 32 | { 33 | LCLineChartData *d1x = ({ 34 | LCLineChartData *d1 = [LCLineChartData new]; 35 | // el-cheapo next/prev day. Don't use this in your Real Code (use NSDateComponents or objc-utils instead) 36 | NSDate *date1 = [[NSDate date] dateByAddingTimeInterval:((-3) * SECS_PER_DAY)]; 37 | NSDate *date2 = [[NSDate date] dateByAddingTimeInterval:((2) * SECS_PER_DAY)]; 38 | d1.xMin = [date1 timeIntervalSinceReferenceDate]; 39 | d1.xMax = [date2 timeIntervalSinceReferenceDate]; 40 | d1.title = @"Foobarbang"; 41 | d1.color = [UIColor redColor]; 42 | d1.itemCount = 6; 43 | NSMutableArray *arr = [NSMutableArray array]; 44 | for(NSUInteger i = 0; i < 4; ++i) { 45 | [arr addObject:@(d1.xMin + (rand() / (float)RAND_MAX) * (d1.xMax - d1.xMin))]; 46 | } 47 | [arr addObject:@(d1.xMin)]; 48 | [arr addObject:@(d1.xMax)]; 49 | [arr sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { 50 | return [obj1 compare:obj2]; 51 | }]; 52 | NSMutableArray *arr2 = [NSMutableArray array]; 53 | for(NSUInteger i = 0; i < 6; ++i) { 54 | [arr2 addObject:@((rand() / (float)RAND_MAX) * 6)]; 55 | } 56 | d1.getData = ^(NSUInteger item) { 57 | float x = [arr[item] floatValue]; 58 | float y = [arr2[item] floatValue]; 59 | NSString *label1 = [self.formatter stringFromDate:[date1 dateByAddingTimeInterval:x]]; 60 | NSString *label2 = [NSString stringWithFormat:@"%f", y]; 61 | return [LCLineChartDataItem dataItemWithX:x y:y xLabel:label1 dataLabel:label2]; 62 | }; 63 | 64 | d1; 65 | }); 66 | 67 | LCLineChartData *d2x = ({ 68 | LCLineChartData *d1 = [LCLineChartData new]; 69 | NSDate *date1 = [[NSDate date] dateByAddingTimeInterval:((-3) * SECS_PER_DAY)]; 70 | NSDate *date2 = [[NSDate date] dateByAddingTimeInterval:((2) * SECS_PER_DAY)]; 71 | d1.xMin = [date1 timeIntervalSinceReferenceDate]; 72 | d1.xMax = [date2 timeIntervalSinceReferenceDate]; 73 | d1.title = @"Bar"; 74 | d1.color = [UIColor blueColor]; 75 | d1.itemCount = 8; 76 | NSMutableArray *arr = [NSMutableArray array]; 77 | for(NSUInteger i = 0; i < d1.itemCount - 2; ++i) { 78 | [arr addObject:@(d1.xMin + (rand() / (float)RAND_MAX) * (d1.xMax - d1.xMin))]; 79 | } 80 | [arr addObject:@(d1.xMin)]; 81 | [arr addObject:@(d1.xMax)]; 82 | [arr sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { 83 | return [obj1 compare:obj2]; 84 | }]; 85 | NSMutableArray *arr2 = [NSMutableArray array]; 86 | for(NSUInteger i = 0; i < d1.itemCount; ++i) { 87 | [arr2 addObject:@((rand() / (float)RAND_MAX) * 6)]; 88 | } 89 | d1.getData = ^(NSUInteger item) { 90 | float x = [arr[item] floatValue]; 91 | float y = [arr2[item] floatValue]; 92 | NSString *label1 = [self.formatter stringFromDate:[date1 dateByAddingTimeInterval:x]]; 93 | NSString *label2 = [NSString stringWithFormat:@"%f", y]; 94 | return [LCLineChartDataItem dataItemWithX:x y:y xLabel:label1 dataLabel:label2]; 95 | }; 96 | 97 | d1; 98 | }); 99 | 100 | LCLineChartView *chartView = [[LCLineChartView alloc] initWithFrame:CGRectMake(20, 400, 500, 300)]; 101 | chartView.yMin = 0; 102 | chartView.yMax = 6; 103 | chartView.ySteps = @[@"1.0",@"2.0",@"3.0",@"4.0",@"5.0",@"A big label at 6.0"]; 104 | chartView.data = @[d1x,d2x]; 105 | chartView.selectedItemCallback = ^(LCLineChartData *dat, NSUInteger item, CGPoint pos) { 106 | if(dat == d1x && item == 2) { 107 | NSLog(@"User selected item 1 in 1st graph at position %@ in the graph view", NSStringFromCGPoint(pos)); 108 | } 109 | }; 110 | 111 | // chartView.drawsDataPoints = NO; // Uncomment to turn off circles at data points. 112 | // chartView.drawsDataLines = NO; // Uncomment to turn off lines connecting data points. (=> scatter plot) 113 | // chartView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0]; // Uncomment for custom background color. 114 | 115 | [self.view addSubview:chartView]; 116 | } 117 | 118 | // second sample chart view 119 | { 120 | LCLineChartData *d = [LCLineChartData new]; 121 | d.xMin = 1; 122 | d.xMax = 31; 123 | d.title = @"The title for the legend"; 124 | d.color = [UIColor redColor]; 125 | d.itemCount = 10; 126 | 127 | NSMutableArray *vals = [NSMutableArray new]; 128 | for(NSUInteger i = 0; i < d.itemCount; ++i) 129 | [vals addObject:@((rand() / (float)RAND_MAX) * (31 - 1) + 1)]; 130 | [vals sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { 131 | return [obj1 compare:obj2]; 132 | }]; 133 | d.getData = ^(NSUInteger item) { 134 | float x = [vals[item] floatValue]; 135 | float y = powf(2, x / 7); 136 | NSString *label1 = [NSString stringWithFormat:@"%lu", (unsigned long)item]; 137 | NSString *label2 = [NSString stringWithFormat:@"%f", y]; 138 | return [LCLineChartDataItem dataItemWithX:x y:y xLabel:label1 dataLabel:label2]; 139 | }; 140 | 141 | LCLineChartView *chartView = [[LCLineChartView alloc] initWithFrame:CGRectMake(20, 700, 500, 300)]; 142 | chartView.yMin = 0; 143 | chartView.yMax = powf(2, 31 / 7) + 0.5; 144 | chartView.ySteps = @[@"0.0", 145 | [NSString stringWithFormat:@"%.02f", chartView.yMax / 2], 146 | [NSString stringWithFormat:@"%.02f", chartView.yMax]]; 147 | chartView.xStepsCount = 5; 148 | chartView.data = @[d]; 149 | 150 | chartView.axisLabelColor = [UIColor blueColor]; 151 | 152 | [self.view addSubview:chartView]; 153 | } 154 | } 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /ios-linechart/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mruegenberg/ios-linechart/a492b9444ab3a45c33c48546451c6f2d073b8f84/ios-linechart/Default-568h@2x.png -------------------------------------------------------------------------------- /ios-linechart/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mruegenberg/ios-linechart/a492b9444ab3a45c33c48546451c6f2d073b8f84/ios-linechart/Default.png -------------------------------------------------------------------------------- /ios-linechart/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mruegenberg/ios-linechart/a492b9444ab3a45c33c48546451c6f2d073b8f84/ios-linechart/Default@2x.png -------------------------------------------------------------------------------- /ios-linechart/LCInfoView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LCInfoView.h 3 | // Classes 4 | // 5 | // Created by Marcel Ruegenberg on 19.11.09. 6 | // Copyright 2009-2011 Dustlab. All rights reserved. 7 | // 8 | 9 | @interface LCInfoView : UIView 10 | 11 | @property CGPoint tapPoint; 12 | 13 | @property (strong) UILabel *infoLabel; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ios-linechart/LCInfoView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCInfoView.m 3 | // Classes 4 | // 5 | // Created by Marcel Ruegenberg on 19.11.09. 6 | // Copyright 2009 Dustlab. All rights reserved. 7 | // 8 | 9 | #import "LCInfoView.h" 10 | #import "UIKit+DrawingHelpers.h" 11 | 12 | 13 | @interface LCInfoView () 14 | 15 | - (void)recalculateFrame; 16 | 17 | @end 18 | 19 | 20 | @implementation LCInfoView 21 | 22 | - (id)initWithFrame:(CGRect)frame { 23 | if ((self = [super initWithFrame:frame])) { 24 | UIFont *fatFont = [UIFont boldSystemFontOfSize:12]; 25 | 26 | self.infoLabel = [[UILabel alloc] init]; self.infoLabel.font = fatFont; 27 | self.infoLabel.backgroundColor = [UIColor clearColor]; self.infoLabel.textColor = [UIColor whiteColor]; 28 | self.infoLabel.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth; 29 | self.infoLabel.shadowColor = [UIColor blackColor]; 30 | self.infoLabel.shadowOffset = CGSizeMake(0, -1); 31 | self.infoLabel.textAlignment = NSTextAlignmentCenter; 32 | [self addSubview:self.infoLabel]; 33 | 34 | self.backgroundColor = [UIColor clearColor]; 35 | } 36 | return self; 37 | } 38 | 39 | - (id)init { 40 | if((self = [self initWithFrame:CGRectZero])) { 41 | ; 42 | } 43 | return self; 44 | } 45 | 46 | #define TOP_BOTTOM_MARGIN 5 47 | #define LEFT_RIGHT_MARGIN 15 48 | #define SHADOWSIZE 3 49 | #define SHADOWBLUR 5 50 | #define HOOK_SIZE 8 51 | 52 | void CGContextAddRoundedRectWithHookSimple(CGContextRef c, CGRect rect, CGFloat radius) { 53 | //eventRect must be relative to rect. 54 | CGFloat hookSize = HOOK_SIZE; 55 | CGContextAddArc(c, rect.origin.x + radius, rect.origin.y + radius, radius, M_PI, M_PI * 1.5, 0); //upper left corner 56 | CGContextAddArc(c, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, radius, M_PI * 1.5, M_PI * 2, 0); //upper right corner 57 | 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); 58 | { 59 | CGContextAddLineToPoint(c, rect.origin.x + rect.size.width / 2 + hookSize, rect.origin.y + rect.size.height); 60 | CGContextAddLineToPoint(c, rect.origin.x + rect.size.width / 2, rect.origin.y + rect.size.height + hookSize); 61 | CGContextAddLineToPoint(c, rect.origin.x + rect.size.width / 2 - hookSize, rect.origin.y + rect.size.height); 62 | } 63 | CGContextAddArc(c, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, M_PI * 0.5, M_PI, 0); 64 | CGContextAddLineToPoint(c, rect.origin.x, rect.origin.y + radius); 65 | } 66 | 67 | - (void)layoutSubviews { 68 | [super layoutSubviews]; 69 | 70 | [self sizeToFit]; 71 | 72 | [self recalculateFrame]; 73 | 74 | [self.infoLabel sizeToFit]; 75 | self.infoLabel.frame = CGRectMake(self.bounds.origin.x + 7, self.bounds.origin.y + 2, self.infoLabel.frame.size.width, self.infoLabel.frame.size.height); 76 | } 77 | 78 | - (CGSize)sizeThatFits:(CGSize)size { 79 | // TODO: replace with new text APIs in iOS 7 only version 80 | #pragma clang diagnostic push 81 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 82 | CGSize s = [self.infoLabel.text sizeWithFont:self.infoLabel.font]; 83 | #pragma clang diagnostic pop 84 | s.height += 15; 85 | s.height += SHADOWSIZE; 86 | 87 | s.width += 2 * SHADOWSIZE + 7; 88 | s.width = MAX(s.width, HOOK_SIZE * 2 + 2 * SHADOWSIZE + 10); 89 | 90 | return s; 91 | } 92 | 93 | - (void)drawRect:(CGRect)rect { 94 | CGContextRef c = UIGraphicsGetCurrentContext(); 95 | 96 | CGRect theRect = self.bounds; 97 | //passe x oder y Position sowie Hoehe oder Breite an, je nachdem, wo der Hook sitzt. 98 | theRect.size.height -= SHADOWSIZE * 2; 99 | theRect.origin.x += SHADOWSIZE; 100 | theRect.size.width -= SHADOWSIZE * 2; 101 | theRect.size.height -= SHADOWSIZE * 2; 102 | 103 | [[UIColor colorWithWhite:0.0 alpha:1.0] set]; 104 | CGContextSetAlpha(c, 0.7); 105 | 106 | CGContextSaveGState(c); 107 | 108 | CGContextSetShadow(c, CGSizeMake(0.0, SHADOWSIZE), SHADOWBLUR); 109 | 110 | CGContextBeginPath(c); 111 | CGContextAddRoundedRectWithHookSimple(c, theRect, 7); 112 | CGContextFillPath(c); 113 | 114 | [[UIColor whiteColor] set]; 115 | theRect.origin.x += 1; 116 | theRect.origin.y += 1; 117 | theRect.size.width -= 2; 118 | theRect.size.height = theRect.size.height / 2 + 1; 119 | CGContextSetAlpha(c, 0.2); 120 | CGContextFillRoundedRect(c, theRect, 6); 121 | } 122 | 123 | 124 | 125 | #define MAX_WIDTH 400 126 | // calculate own frame to fit within parent frame and be large enough to hold the event. 127 | - (void)recalculateFrame { 128 | CGRect theFrame = self.frame; 129 | theFrame.size.width = MIN(MAX_WIDTH, theFrame.size.width); 130 | 131 | CGRect theRect = self.frame; theRect.origin = CGPointZero; 132 | 133 | { 134 | theFrame.origin.y = self.tapPoint.y - theFrame.size.height + 2 * SHADOWSIZE + 1; 135 | theFrame.origin.x = round(self.tapPoint.x - ((theFrame.size.width - 2 * SHADOWSIZE)) / 2.0); 136 | } 137 | self.frame = theFrame; 138 | } 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /ios-linechart/LCLegendView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LCLegendView.h 3 | // ios-linechart 4 | // 5 | // Created by Marcel Ruegenberg on 02.08.13. 6 | // Copyright (c) 2013 Marcel Ruegenberg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface LCLegendView : UIView 13 | 14 | @property (nonatomic, strong) UIFont *titlesFont; 15 | @property (strong) NSArray *titles; 16 | @property (strong) NSDictionary *colors; // maps titles to UIColors 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ios-linechart/LCLegendView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCLegendView.m 3 | // ios-linechart 4 | // 5 | // Created by Marcel Ruegenberg on 02.08.13. 6 | // Copyright (c) 2013 Marcel Ruegenberg. All rights reserved. 7 | // 8 | 9 | #import "LCLegendView.h" 10 | #import "UIKit+DrawingHelpers.h" 11 | #import 12 | 13 | @implementation LCLegendView 14 | @synthesize titlesFont=_titlesFont; 15 | 16 | #define COLORPADDING 15 17 | #define PADDING 5 18 | 19 | - (void)drawRect:(CGRect)rect { 20 | CGContextRef c = UIGraphicsGetCurrentContext(); 21 | CGContextSetFillColorWithColor(c, [[UIColor colorWithWhite:0.0 alpha:0.1] CGColor]); 22 | CGContextFillRoundedRect(c, self.bounds, 7); 23 | 24 | 25 | CGFloat y = 0; 26 | for(NSString *title in self.titles) { 27 | UIColor *color = [self.colors objectForKey:title]; 28 | if(color) { 29 | [color setFill]; 30 | CGContextFillEllipseInRect(c, CGRectMake(PADDING + 2, PADDING + round(y) + self.titlesFont.xHeight / 2 + 1, 6, 6)); 31 | } 32 | [[UIColor whiteColor] set]; 33 | // TODO: replace with new text APIs in iOS 7 only version 34 | #pragma clang diagnostic push 35 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 36 | [title drawAtPoint:CGPointMake(COLORPADDING + PADDING, y + PADDING + 1) withFont:self.titlesFont]; 37 | [[UIColor blackColor] set]; 38 | [title drawAtPoint:CGPointMake(COLORPADDING + PADDING, y + PADDING) withFont:self.titlesFont]; 39 | #pragma clang diagnostic pop 40 | y += [self.titlesFont lineHeight]; 41 | } 42 | } 43 | 44 | - (UIFont *)titlesFont { 45 | if(_titlesFont == nil) 46 | _titlesFont = [UIFont boldSystemFontOfSize:10]; 47 | return _titlesFont; 48 | } 49 | 50 | - (CGSize)sizeThatFits:(CGSize)size { 51 | CGFloat h = [self.titlesFont lineHeight] * [self.titles count]; 52 | CGFloat w = 0; 53 | for(NSString *title in self.titles) { 54 | // TODO: replace with new text APIs in iOS 7 only version 55 | #pragma clang diagnostic push 56 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 57 | CGSize s = [title sizeWithFont:self.titlesFont]; 58 | #pragma clang diagnostic pop 59 | w = MAX(w, s.width); 60 | } 61 | return CGSizeMake(COLORPADDING + w + 2 * PADDING, h + 2 * PADDING); 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /ios-linechart/LCLineChartView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LCLineChartView.h 3 | // 4 | // 5 | // Created by Marcel Ruegenberg on 02.08.13. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @class LCLineChartDataItem; 12 | @class LCLineChartData; 13 | 14 | typedef LCLineChartDataItem *(^LCLineChartDataGetter)(NSUInteger item); 15 | typedef void(^LCLineChartSelectedItem)(LCLineChartData * data, NSUInteger item, CGPoint positionInChart); 16 | typedef void(^LCLineChartDeselectedItem)(); 17 | 18 | 19 | @interface LCLineChartDataItem : NSObject 20 | 21 | @property (readonly) double x; /// should be within the x range 22 | @property (readonly) double y; /// should be within the y range 23 | @property (readonly) NSString *xLabel; /// label to be shown on the x axis 24 | @property (readonly) NSString *dataLabel; /// label to be shown directly at the data item 25 | 26 | + (LCLineChartDataItem *)dataItemWithX:(double)x y:(double)y xLabel:(NSString *)xLabel dataLabel:(NSString *)dataLabel; 27 | 28 | @end 29 | 30 | 31 | 32 | @interface LCLineChartData : NSObject 33 | 34 | @property BOOL drawsDataPoints; 35 | @property (strong) UIColor *color; 36 | @property (copy) NSString *title; 37 | @property NSUInteger itemCount; 38 | 39 | @property double xMin; 40 | @property double xMax; 41 | 42 | @property (copy) LCLineChartDataGetter getData; 43 | 44 | @end 45 | 46 | 47 | 48 | @interface LCLineChartView : UIView 49 | 50 | @property (copy) LCLineChartSelectedItem selectedItemCallback; /// Called whenever a data point is selected 51 | @property (copy) LCLineChartDeselectedItem deselectedItemCallback; /// Called after a data point is deselected and before the next `selected` callback 52 | 53 | @property (nonatomic, strong) NSArray *data; /// Array of `LineChartData` objects, one for each line. 54 | 55 | @property double yMin; 56 | @property double yMax; 57 | @property (strong) NSArray *ySteps; /// Array of step names (NSString). At each step, a scale line is shown. 58 | @property NSUInteger xStepsCount; /// number of steps in x. At each x step, a vertical scale line is shown. if x < 2, nothing is done 59 | 60 | @property BOOL smoothPlot; /// draw a smoothed Bezier plot? Default: NO 61 | @property BOOL drawsDataPoints; /// Switch to turn off circles on data points. On by default. 62 | @property BOOL drawsDataLines; /// Switch to turn off lines connecting data points. On by default. 63 | 64 | @property (strong) UIFont *scaleFont; /// Font in which scale markings are drawn. Defaults to [UIFont systemFontOfSize:10]. 65 | @property (nonatomic,strong) UIColor *axisLabelColor; 66 | 67 | - (void)showLegend:(BOOL)show animated:(BOOL)animated; 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /ios-linechart/LCLineChartView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCLineChartView.m 3 | // 4 | // 5 | // Created by Marcel Ruegenberg on 02.08.13. 6 | // 7 | // 8 | 9 | #import "LCLineChartView.h" 10 | #import "LCLegendView.h" 11 | #import "LCInfoView.h" 12 | 13 | @interface LCLineChartDataItem () 14 | 15 | @property (readwrite) double x; // should be within the x range 16 | @property (readwrite) double y; // should be within the y range 17 | @property (readwrite) NSString *xLabel; // label to be shown on the x axis 18 | @property (readwrite) NSString *dataLabel; // label to be shown directly at the data item 19 | 20 | - (id)initWithhX:(double)x y:(double)y xLabel:(NSString *)xLabel dataLabel:(NSString *)dataLabel; 21 | 22 | @end 23 | 24 | @implementation LCLineChartDataItem 25 | 26 | - (id)initWithhX:(double)x y:(double)y xLabel:(NSString *)xLabel dataLabel:(NSString *)dataLabel { 27 | if((self = [super init])) { 28 | self.x = x; 29 | self.y = y; 30 | self.xLabel = xLabel; 31 | self.dataLabel = dataLabel; 32 | } 33 | return self; 34 | } 35 | 36 | + (LCLineChartDataItem *)dataItemWithX:(double)x y:(double)y xLabel:(NSString *)xLabel dataLabel:(NSString *)dataLabel { 37 | return [[LCLineChartDataItem alloc] initWithhX:x y:y xLabel:xLabel dataLabel:dataLabel]; 38 | } 39 | 40 | @end 41 | 42 | 43 | 44 | @implementation LCLineChartData 45 | 46 | - (id)init { 47 | self = [super init]; 48 | if(self) { 49 | self.drawsDataPoints = YES; 50 | } 51 | return self; 52 | } 53 | 54 | @end 55 | 56 | 57 | 58 | @interface LCLineChartView () 59 | 60 | @property LCLegendView *legendView; 61 | @property LCInfoView *infoView; 62 | @property UIView *currentPosView; 63 | @property UILabel *xAxisLabel; 64 | 65 | - (BOOL)drawsAnyData; 66 | 67 | @property LCLineChartData *selectedData; 68 | @property NSUInteger selectedIdx; 69 | 70 | @end 71 | 72 | 73 | #define X_AXIS_SPACE 15 74 | #define PADDING 10 75 | 76 | 77 | @implementation LCLineChartView 78 | @synthesize data=_data; 79 | 80 | - (void)setDefaultValues { 81 | self.currentPosView = [[UIView alloc] initWithFrame:CGRectMake(PADDING, PADDING, 1 / self.contentScaleFactor, 50)]; 82 | self.currentPosView.backgroundColor = [UIColor colorWithRed:0.7 green:0.0 blue:0.0 alpha:1.0]; 83 | self.currentPosView.autoresizingMask = UIViewAutoresizingFlexibleHeight; 84 | self.currentPosView.alpha = 0.0; 85 | [self addSubview:self.currentPosView]; 86 | 87 | self.legendView = [[LCLegendView alloc] init]; 88 | self.legendView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleBottomMargin; 89 | self.legendView.backgroundColor = [UIColor clearColor]; 90 | [self addSubview:self.legendView]; 91 | 92 | self.axisLabelColor = [UIColor grayColor]; 93 | 94 | self.xAxisLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 50, 20)]; 95 | self.xAxisLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 96 | self.xAxisLabel.font = [UIFont boldSystemFontOfSize:10]; 97 | self.xAxisLabel.textColor = self.axisLabelColor; 98 | self.xAxisLabel.textAlignment = NSTextAlignmentCenter; 99 | self.xAxisLabel.alpha = 0.0; 100 | self.xAxisLabel.backgroundColor = [UIColor clearColor]; 101 | [self addSubview:self.xAxisLabel]; 102 | 103 | self.backgroundColor = [UIColor whiteColor]; 104 | self.scaleFont = [UIFont systemFontOfSize:10.0]; 105 | 106 | self.autoresizesSubviews = YES; 107 | self.contentMode = UIViewContentModeRedraw; 108 | 109 | self.drawsDataPoints = YES; 110 | self.drawsDataLines = YES; 111 | 112 | self.selectedIdx = INT_MAX; 113 | } 114 | 115 | - (id)initWithCoder:(NSCoder *)aDecoder { 116 | if((self = [super initWithCoder:aDecoder])) { 117 | [self setDefaultValues]; 118 | } 119 | return self; 120 | } 121 | 122 | - (id)initWithFrame:(CGRect)frame { 123 | if((self = [super initWithFrame:frame])) { 124 | [self setDefaultValues]; 125 | } 126 | return self; 127 | } 128 | 129 | - (void)setAxisLabelColor:(UIColor *)axisLabelColor { 130 | if(axisLabelColor != _axisLabelColor) { 131 | [self willChangeValueForKey:@"axisLabelColor"]; 132 | _axisLabelColor = axisLabelColor; 133 | self.xAxisLabel.textColor = axisLabelColor; 134 | [self didChangeValueForKey:@"axisLabelColor"]; 135 | } 136 | } 137 | 138 | - (void)showLegend:(BOOL)show animated:(BOOL)animated { 139 | if(! animated) { 140 | self.legendView.alpha = show ? 1.0 : 0.0; 141 | return; 142 | } 143 | 144 | [UIView animateWithDuration:0.3 animations:^{ 145 | self.legendView.alpha = show ? 1.0 : 0.0; 146 | }]; 147 | } 148 | 149 | - (void)layoutSubviews { 150 | [self.legendView sizeToFit]; 151 | CGRect r = self.legendView.frame; 152 | r.origin.x = self.bounds.size.width - self.legendView.frame.size.width - 3 - PADDING; 153 | r.origin.y = 3 + PADDING; 154 | self.legendView.frame = r; 155 | 156 | r = self.currentPosView.frame; 157 | CGFloat h = self.bounds.size.height; 158 | r.size.height = h - 2 * PADDING - X_AXIS_SPACE; 159 | self.currentPosView.frame = r; 160 | 161 | [self.xAxisLabel sizeToFit]; 162 | r = self.xAxisLabel.frame; 163 | r.origin.y = self.bounds.size.height - X_AXIS_SPACE - PADDING + 2; 164 | self.xAxisLabel.frame = r; 165 | 166 | [self bringSubviewToFront:self.legendView]; 167 | } 168 | 169 | - (void)setData:(NSArray *)data { 170 | if(data != _data) { 171 | NSMutableArray *titles = [NSMutableArray arrayWithCapacity:[data count]]; 172 | NSMutableDictionary *colors = [NSMutableDictionary dictionaryWithCapacity:[data count]]; 173 | for(LCLineChartData *dat in data) { 174 | NSString *key = dat.title; 175 | if(key == nil) key = @""; 176 | [titles addObject:key]; 177 | [colors setObject:dat.color forKey:key]; 178 | } 179 | self.legendView.titles = titles; 180 | self.legendView.colors = colors; 181 | self.selectedData = nil; 182 | self.selectedIdx = INT_MAX; 183 | 184 | _data = data; 185 | 186 | [self setNeedsDisplay]; 187 | } 188 | } 189 | 190 | - (void)drawRect:(CGRect)rect { 191 | [super drawRect:rect]; 192 | 193 | CGContextRef c = UIGraphicsGetCurrentContext(); 194 | 195 | CGFloat availableHeight = self.bounds.size.height - 2 * PADDING - X_AXIS_SPACE; 196 | 197 | CGFloat availableWidth = self.bounds.size.width - 2 * PADDING - self.yAxisLabelsWidth; 198 | CGFloat xStart = PADDING + self.yAxisLabelsWidth; 199 | CGFloat yStart = PADDING; 200 | 201 | static CGFloat dashedPattern[] = {4,2}; 202 | 203 | // draw scale and horizontal lines 204 | CGFloat heightPerStep = self.ySteps == nil || [self.ySteps count] <= 1 ? availableHeight : (availableHeight / ([self.ySteps count] - 1)); 205 | 206 | NSUInteger i = 0; 207 | CGContextSaveGState(c); 208 | CGContextSetLineWidth(c, 1.0); 209 | NSUInteger yCnt = [self.ySteps count]; 210 | for(NSString *step in self.ySteps) { 211 | [self.axisLabelColor set]; 212 | CGFloat h = [self.scaleFont lineHeight]; 213 | CGFloat y = yStart + heightPerStep * (yCnt - 1 - i); 214 | // TODO: replace with new text APIs in iOS 7 only version 215 | #pragma clang diagnostic push 216 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 217 | [step drawInRect:CGRectMake(yStart, y - h / 2, self.yAxisLabelsWidth - 6, h) withFont:self.scaleFont lineBreakMode:NSLineBreakByClipping alignment:NSTextAlignmentRight]; 218 | #pragma clagn diagnostic pop 219 | 220 | [[UIColor colorWithWhite:0.9 alpha:1.0] set]; 221 | CGContextSetLineDash(c, 0, dashedPattern, 2); 222 | CGContextMoveToPoint(c, xStart, round(y) + 0.5); 223 | CGContextAddLineToPoint(c, self.bounds.size.width - PADDING, round(y) + 0.5); 224 | CGContextStrokePath(c); 225 | 226 | i++; 227 | } 228 | 229 | NSUInteger xCnt = self.xStepsCount; 230 | if(xCnt > 1) { 231 | CGFloat widthPerStep = availableWidth / (xCnt - 1); 232 | 233 | [[UIColor grayColor] set]; 234 | for(NSUInteger i = 0; i < xCnt; ++i) { 235 | CGFloat x = xStart + widthPerStep * (xCnt - 1 - i); 236 | 237 | [[UIColor colorWithWhite:0.9 alpha:1.0] set]; 238 | CGContextMoveToPoint(c, round(x) + 0.5, PADDING); 239 | CGContextAddLineToPoint(c, round(x) + 0.5, yStart + availableHeight); 240 | CGContextStrokePath(c); 241 | } 242 | } 243 | 244 | CGContextRestoreGState(c); 245 | 246 | 247 | if (!self.drawsAnyData) { 248 | NSLog(@"You configured LineChartView to draw neither lines nor data points. No data will be visible. This is most likely not what you wanted. (But we aren't judging you, so here's your chart background.)"); 249 | } // warn if no data will be drawn 250 | 251 | CGFloat yRangeLen = self.yMax - self.yMin; 252 | if(yRangeLen == 0) yRangeLen = 1; 253 | for(LCLineChartData *data in self.data) { 254 | if (self.drawsDataLines) { 255 | double xRangeLen = data.xMax - data.xMin; 256 | if(xRangeLen == 0) xRangeLen = 1; 257 | if(data.itemCount >= 2) { 258 | LCLineChartDataItem *datItem = data.getData(0); 259 | CGMutablePathRef path = CGPathCreateMutable(); 260 | CGFloat prevX = xStart + round(((datItem.x - data.xMin) / xRangeLen) * availableWidth); 261 | CGFloat prevY = yStart + round((1.0 - (datItem.y - self.yMin) / yRangeLen) * availableHeight); 262 | CGPathMoveToPoint(path, NULL, prevX, prevY); 263 | for(NSUInteger i = 1; i < data.itemCount; ++i) { 264 | LCLineChartDataItem *datItem = data.getData(i); 265 | CGFloat x = xStart + round(((datItem.x - data.xMin) / xRangeLen) * availableWidth); 266 | CGFloat y = yStart + round((1.0 - (datItem.y - self.yMin) / yRangeLen) * availableHeight); 267 | CGFloat xDiff = x - prevX; 268 | CGFloat yDiff = y - prevY; 269 | 270 | if(xDiff != 0) { 271 | CGFloat xSmoothing = self.smoothPlot ? MIN(30,xDiff) : 0; 272 | CGFloat ySmoothing = 0.5; 273 | CGFloat slope = yDiff / xDiff; 274 | CGPoint controlPt1 = CGPointMake(prevX + xSmoothing, prevY + ySmoothing * slope * xSmoothing); 275 | CGPoint controlPt2 = CGPointMake(x - xSmoothing, y - ySmoothing * slope * xSmoothing); 276 | CGPathAddCurveToPoint(path, NULL, controlPt1.x, controlPt1.y, controlPt2.x, controlPt2.y, x, y); 277 | } 278 | else { 279 | CGPathAddLineToPoint(path, NULL, x, y); 280 | } 281 | prevX = x; 282 | prevY = y; 283 | } 284 | 285 | CGContextAddPath(c, path); 286 | CGContextSetStrokeColorWithColor(c, [self.backgroundColor CGColor]); 287 | CGContextSetLineWidth(c, 5); 288 | CGContextStrokePath(c); 289 | 290 | CGContextAddPath(c, path); 291 | CGContextSetStrokeColorWithColor(c, [data.color CGColor]); 292 | CGContextSetLineWidth(c, 2); 293 | CGContextStrokePath(c); 294 | 295 | CGPathRelease(path); 296 | } 297 | } // draw actual chart data 298 | if (self.drawsDataPoints) { 299 | if (data.drawsDataPoints) { 300 | double xRangeLen = data.xMax - data.xMin; 301 | if(xRangeLen == 0) xRangeLen = 1; 302 | for(NSUInteger i = 0; i < data.itemCount; ++i) { 303 | LCLineChartDataItem *datItem = data.getData(i); 304 | CGFloat xVal = xStart + round((xRangeLen == 0 ? 0.5 : ((datItem.x - data.xMin) / xRangeLen)) * availableWidth); 305 | CGFloat yVal = yStart + round((1.0 - (datItem.y - self.yMin) / yRangeLen) * availableHeight); 306 | [self.backgroundColor setFill]; 307 | CGContextFillEllipseInRect(c, CGRectMake(xVal - 5.5, yVal - 5.5, 11, 11)); 308 | [data.color setFill]; 309 | CGContextFillEllipseInRect(c, CGRectMake(xVal - 4, yVal - 4, 8, 8)); 310 | { 311 | CGFloat brightness; 312 | CGFloat r,g,b,a; 313 | if(CGColorGetNumberOfComponents([data.color CGColor]) < 3) 314 | [data.color getWhite:&brightness alpha:&a]; 315 | else { 316 | [data.color getRed:&r green:&g blue:&b alpha:&a]; 317 | brightness = 0.299 * r + 0.587 * g + 0.114 * b; // RGB ~> Luma conversion 318 | } 319 | if(brightness <= 0.68) // basically arbitrary, but works well for test cases 320 | [[UIColor whiteColor] setFill]; 321 | else 322 | [[UIColor blackColor] setFill]; 323 | } 324 | CGContextFillEllipseInRect(c, CGRectMake(xVal - 2, yVal - 2, 4, 4)); 325 | } // for 326 | } // data - draw data points 327 | } // draw data points 328 | } 329 | } 330 | 331 | - (void)showIndicatorForTouch:(UITouch *)touch { 332 | if(! self.infoView) { 333 | self.infoView = [[LCInfoView alloc] init]; 334 | [self addSubview:self.infoView]; 335 | } 336 | 337 | CGPoint pos = [touch locationInView:self]; 338 | CGFloat xStart = PADDING + self.yAxisLabelsWidth; 339 | CGFloat yStart = PADDING; 340 | CGFloat yRangeLen = self.yMax - self.yMin; 341 | if(yRangeLen == 0) yRangeLen = 1; 342 | CGFloat xPos = pos.x - xStart; 343 | CGFloat yPos = pos.y - yStart; 344 | CGFloat availableWidth = self.bounds.size.width - 2 * PADDING - self.yAxisLabelsWidth; 345 | CGFloat availableHeight = self.bounds.size.height - 2 * PADDING - X_AXIS_SPACE; 346 | 347 | LCLineChartDataItem *closest = nil; 348 | LCLineChartData *closestData = nil; 349 | NSUInteger closestIdx = INT_MAX; 350 | double minDist = DBL_MAX; 351 | double minDistY = DBL_MAX; 352 | CGPoint closestPos = CGPointZero; 353 | 354 | for(LCLineChartData *data in self.data) { 355 | double xRangeLen = data.xMax - data.xMin; 356 | 357 | // note: if necessary, could use binary search here to speed things up 358 | for(NSUInteger i = 0; i < data.itemCount; ++i) { 359 | LCLineChartDataItem *datItem = data.getData(i); 360 | CGFloat xVal = round((xRangeLen == 0 ? 0.0 : ((datItem.x - data.xMin) / xRangeLen)) * availableWidth); 361 | CGFloat yVal = round((1.0 - (datItem.y - self.yMin) / yRangeLen) * availableHeight); 362 | 363 | double dist = fabsf(xVal - xPos); 364 | double distY = fabsf(yVal - yPos); 365 | if(dist < minDist || (dist == minDist && distY < minDistY)) { 366 | minDist = dist; 367 | minDistY = distY; 368 | closest = datItem; 369 | closestData = data; 370 | closestIdx = i; 371 | closestPos = CGPointMake(xStart + xVal - 3, yStart + yVal - 7); 372 | } 373 | } 374 | } 375 | 376 | if(closest == nil || (closestData == self.selectedData && closestIdx == self.selectedIdx)) 377 | return; 378 | 379 | self.selectedData = closestData; 380 | self.selectedIdx = closestIdx; 381 | 382 | self.infoView.infoLabel.text = closest.dataLabel; 383 | self.infoView.tapPoint = closestPos; 384 | [self.infoView sizeToFit]; 385 | [self.infoView setNeedsLayout]; 386 | [self.infoView setNeedsDisplay]; 387 | 388 | if(self.currentPosView.alpha == 0.0) { 389 | CGRect r = self.currentPosView.frame; 390 | r.origin.x = closestPos.x + 3 - 1; 391 | self.currentPosView.frame = r; 392 | } 393 | 394 | [UIView animateWithDuration:0.1 animations:^{ 395 | self.infoView.alpha = 1.0; 396 | self.currentPosView.alpha = 1.0; 397 | self.xAxisLabel.alpha = 1.0; 398 | 399 | CGRect r = self.currentPosView.frame; 400 | r.origin.x = closestPos.x + 3 - 1; 401 | self.currentPosView.frame = r; 402 | 403 | self.xAxisLabel.text = closest.xLabel; 404 | if(self.xAxisLabel.text != nil) { 405 | [self.xAxisLabel sizeToFit]; 406 | r = self.xAxisLabel.frame; 407 | r.origin.x = round(closestPos.x - r.size.width / 2); 408 | self.xAxisLabel.frame = r; 409 | } 410 | }]; 411 | 412 | if(self.selectedItemCallback != nil) { 413 | self.selectedItemCallback(closestData, closestIdx, closestPos); 414 | } 415 | } 416 | 417 | - (void)hideIndicator { 418 | if(self.deselectedItemCallback) 419 | self.deselectedItemCallback(); 420 | 421 | self.selectedData = nil; 422 | 423 | [UIView animateWithDuration:0.1 animations:^{ 424 | self.infoView.alpha = 0.0; 425 | self.currentPosView.alpha = 0.0; 426 | self.xAxisLabel.alpha = 0.0; 427 | }]; 428 | } 429 | 430 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 431 | [self showIndicatorForTouch:[touches anyObject]]; 432 | } 433 | 434 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 435 | [self showIndicatorForTouch:[touches anyObject]]; 436 | } 437 | 438 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 439 | [self hideIndicator]; 440 | } 441 | 442 | - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { 443 | [self hideIndicator]; 444 | } 445 | 446 | 447 | #pragma mark Helper methods 448 | 449 | - (BOOL)drawsAnyData { 450 | return self.drawsDataPoints || self.drawsDataLines; 451 | } 452 | 453 | // TODO: This should really be a cached value. Invalidated iff ySteps changes. 454 | - (CGFloat)yAxisLabelsWidth { 455 | double maxV = 0; 456 | for(NSString *label in self.ySteps) { 457 | CGSize labelSize = [label sizeWithFont:self.scaleFont]; 458 | if(labelSize.width > maxV) maxV = labelSize.width; 459 | } 460 | return maxV + PADDING; 461 | } 462 | 463 | @end 464 | -------------------------------------------------------------------------------- /ios-linechart/LineChart.h: -------------------------------------------------------------------------------- 1 | // 2 | // LineChart.h 3 | // ios-linechart 4 | // 5 | // Created by Marcel Ruegenberg on 22.10.13. 6 | // Copyright (c) 2013 Marcel Ruegenberg. All rights reserved. 7 | // 8 | 9 | #import "LCInfoView.h" 10 | #import "LCLineChartView.h" 11 | #import "LCLegendView.h" 12 | 13 | typedef LCLegendView LegendView; 14 | typedef LCLineChartView LineChartView; 15 | typedef LCInfoView InfoView; 16 | typedef LCLineChartData LineChartData; 17 | typedef LCLineChartDataItem LineChartDataItem; 18 | typedef LCLineChartDataGetter LineChartDataGetter; 19 | typedef LCLineChartSelectedPoint LineChartSelectedPoint; 20 | typedef LCLineChartDeselectedPoint LineChartDeselectedPoint; 21 | 22 | -------------------------------------------------------------------------------- /ios-linechart/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /ios-linechart/en.lproj/MainStoryboard.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 | -------------------------------------------------------------------------------- /ios-linechart/ios-linechart-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.dustlab.${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 | MainStoryboard 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations~ipad 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationPortraitUpsideDown 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ios-linechart/ios-linechart-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'ios-linechart' target in the 'ios-linechart' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /ios-linechart/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ios-linechart 4 | // 5 | // Created by Marcel Ruegenberg on 02.08.13. 6 | // Copyright (c) 2013 Marcel Ruegenberg. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "ChartAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([ChartAppDelegate class])); 17 | } 18 | } 19 | --------------------------------------------------------------------------------