From 19165ddacb2bbac9db0a541fd92f86b294147c11 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Fri, 20 Mar 2015 13:15:10 +0200 Subject: [PATCH] Initial code commit --- Charts/Charts.xcodeproj/project.pbxproj | 626 ++++++++ Charts/Classes/Animation/ChartAnimator.swift | 149 ++ Charts/Classes/Charts/BarChartView.swift | 321 ++++ .../Classes/Charts/BarLineChartViewBase.swift | 1338 +++++++++++++++++ .../Classes/Charts/CandleStickChartView.swift | 76 + Charts/Classes/Charts/ChartViewBase.swift | 738 +++++++++ Charts/Classes/Charts/CombinedChartView.swift | 186 +++ .../Charts/HorizontalBarChartView.swift | 164 ++ Charts/Classes/Charts/LineChartView.swift | 109 ++ Charts/Classes/Charts/PieChartView.swift | 442 ++++++ .../Charts/PieRadarChartViewBase.swift | 504 +++++++ Charts/Classes/Charts/RadarChartView.swift | 240 +++ Charts/Classes/Charts/ScatterChartView.swift | 81 + Charts/Classes/Components/ChartAxisBase.swift | 57 + .../Components/ChartComponentBase.swift | 29 + Charts/Classes/Components/ChartLegend.swift | 223 +++ .../Classes/Components/ChartLimitLine.swift | 76 + Charts/Classes/Components/ChartMarker.swift | 57 + Charts/Classes/Components/ChartXAxis.swift | 83 + Charts/Classes/Components/ChartYAxis.swift | 246 +++ Charts/Classes/Data/BarChartData.swift | 42 + Charts/Classes/Data/BarChartDataEntry.swift | 109 ++ Charts/Classes/Data/BarChartDataSet.swift | 113 ++ .../Data/BarLineScatterCandleChartData.swift | 32 + .../BarLineScatterCandleChartDataSet.swift | 35 + Charts/Classes/Data/CandleChartData.swift | 19 + .../Classes/Data/CandleChartDataEntry.swift | 86 ++ Charts/Classes/Data/CandleChartDataSet.swift | 81 + Charts/Classes/Data/ChartData.swift | 831 ++++++++++ Charts/Classes/Data/ChartDataEntry.swift | 129 ++ Charts/Classes/Data/ChartDataSet.swift | 390 +++++ Charts/Classes/Data/CombinedChartData.swift | 136 ++ Charts/Classes/Data/LineChartData.swift | 20 + Charts/Classes/Data/LineChartDataSet.swift | 116 ++ .../Classes/Data/LineRadarChartDataSet.swift | 60 + Charts/Classes/Data/PieChartData.swift | 75 + Charts/Classes/Data/PieChartDataSet.swift | 71 + Charts/Classes/Data/RadarChartData.swift | 28 + Charts/Classes/Data/RadarChartDataSet.swift | 32 + Charts/Classes/Data/ScatterChartData.swift | 44 + Charts/Classes/Data/ScatterChartDataSet.swift | 43 + .../Filters/ChartDataApproximatorFilter.swift | 216 +++ .../Classes/Filters/ChartDataBaseFilter.swift | 28 + .../Classes/Renderers/BarChartRenderer.swift | 540 +++++++ .../Renderers/CandleStickChartRenderer.swift | 264 ++++ .../Renderers/ChartAxisRendererBase.swift | 50 + .../Renderers/ChartDataRendererBase.swift | 45 + .../Renderers/ChartLegendRenderer.swift | 432 ++++++ .../Classes/Renderers/ChartRendererBase.swift | 71 + .../Renderers/ChartXAxisRenderer.swift | 233 +++ .../ChartXAxisRendererBarChart.swift | 145 ++ ...ChartXAxisRendererHorizontalBarChart.swift | 196 +++ .../ChartXAxisRendererRadarChart.swift | 55 + .../Renderers/ChartYAxisRenderer.swift | 355 +++++ ...ChartYAxisRendererHorizontalBarChart.swift | 278 ++++ .../ChartYAxisRendererRadarChart.swift | 184 +++ .../Renderers/CombinedChartRenderer.swift | 341 +++++ .../HorizontalBarChartRenderer.swift | 404 +++++ .../Classes/Renderers/LineChartRenderer.swift | 618 ++++++++ .../Classes/Renderers/PieChartRenderer.swift | 367 +++++ .../Renderers/RadarChartRenderer.swift | 280 ++++ .../Renderers/ScatterChartRenderer.swift | 298 ++++ .../Classes/Utils/ChartColorTemplates.swift | 74 + Charts/Classes/Utils/ChartFillFormatter.swift | 22 + Charts/Classes/Utils/ChartHighlight.swift | 121 ++ Charts/Classes/Utils/ChartSelInfo.swift | 113 ++ Charts/Classes/Utils/ChartTransformer.swift | 269 ++++ Charts/Classes/Utils/ChartUtils.swift | 136 ++ .../Classes/Utils/ChartViewPortHandler.swift | 377 +++++ Charts/Supporting Files/Charts.h | 25 + Charts/Supporting Files/Info.plist | 26 + .../ChartsDemo.xcodeproj/project.pbxproj | 582 +++++++ ChartsDemo/Classes/AppDelegate.h | 22 + ChartsDemo/Classes/AppDelegate.m | 58 + .../Classes/Components/BalloonMarker.swift | 109 ++ ChartsDemo/Classes/DemoBaseViewController.h | 26 + ChartsDemo/Classes/DemoBaseViewController.m | 183 +++ ChartsDemo/Classes/DemoListViewController.h | 20 + ChartsDemo/Classes/DemoListViewController.m | 182 +++ ChartsDemo/Classes/DemoListViewController.xib | 37 + .../Demos/AnotherBarChartViewController.h | 20 + .../Demos/AnotherBarChartViewController.m | 201 +++ .../Demos/AnotherBarChartViewController.xib | 91 ++ .../Classes/Demos/BarChartViewController.h | 20 + .../Classes/Demos/BarChartViewController.m | 221 +++ .../Classes/Demos/BarChartViewController.xib | 91 ++ .../Demos/CandleStickChartViewController.h | 20 + .../Demos/CandleStickChartViewController.m | 231 +++ .../Demos/CandleStickChartViewController.xib | 91 ++ .../Demos/ColoredLineChartViewController.h | 20 + .../Demos/ColoredLineChartViewController.m | 123 ++ .../Demos/ColoredLineChartViewController.xib | 61 + .../Demos/CombinedChartViewController.h | 20 + .../Demos/CombinedChartViewController.m | 166 ++ .../Demos/CombinedChartViewController.xib | 47 + .../Demos/CubicLineChartViewController.h | 20 + .../Demos/CubicLineChartViewController.m | 229 +++ .../Demos/CubicLineChartViewController.xib | 91 ++ .../Demos/HorizontalBarChartViewController.h | 20 + .../Demos/HorizontalBarChartViewController.m | 219 +++ .../HorizontalBarChartViewController.xib | 91 ++ .../Classes/Demos/LineChart1ViewController.h | 20 + .../Classes/Demos/LineChart1ViewController.m | 255 ++++ .../Demos/LineChart1ViewController.xib | 91 ++ .../Classes/Demos/LineChart2ViewController.h | 20 + .../Classes/Demos/LineChart2ViewController.m | 275 ++++ .../Demos/LineChart2ViewController.xib | 91 ++ .../Demos/MultipleBarChartViewController.h | 20 + .../Demos/MultipleBarChartViewController.m | 229 +++ .../Demos/MultipleBarChartViewController.xib | 91 ++ .../Demos/MultipleLinesChartViewController.h | 20 + .../Demos/MultipleLinesChartViewController.m | 232 +++ .../MultipleLinesChartViewController.xib | 91 ++ .../Classes/Demos/PieChartViewController.h | 20 + .../Classes/Demos/PieChartViewController.m | 216 +++ .../Classes/Demos/PieChartViewController.xib | 91 ++ .../Classes/Demos/RadarChartViewController.h | 20 + .../Classes/Demos/RadarChartViewController.m | 183 +++ .../Demos/RadarChartViewController.xib | 47 + .../Demos/ScatterChartViewController.h | 20 + .../Demos/ScatterChartViewController.m | 249 +++ .../Demos/ScatterChartViewController.xib | 91 ++ .../Demos/SinusBarChartViewController.h | 20 + .../Demos/SinusBarChartViewController.m | 210 +++ .../Demos/SinusBarChartViewController.xib | 69 + .../Demos/StackedBarChartViewController.h | 20 + .../Demos/StackedBarChartViewController.m | 224 +++ .../Demos/StackedBarChartViewController.xib | 91 ++ .../AppIcon.appiconset/Contents.json | 44 + .../AppIcon.appiconset/Icon-29@2x.png | Bin 0 -> 5677 bytes .../AppIcon.appiconset/Icon-29@3x.png | Bin 0 -> 10210 bytes .../AppIcon.appiconset/Icon-40@2x.png | Bin 0 -> 9266 bytes .../AppIcon.appiconset/Icon-40@3x.png | Bin 0 -> 14343 bytes .../AppIcon.appiconset/Icon-60@2x.png | Bin 0 -> 14343 bytes .../AppIcon.appiconset/Icon-60@3x.png | Bin 0 -> 27502 bytes .../LaunchImage.launchimage/Contents.json | 43 + .../Default-568h@2x.png | Bin 0 -> 16567 bytes .../Default-667h@2x.png | Bin 0 -> 22355 bytes .../Default-736h@3x.png | Bin 0 -> 55285 bytes .../LaunchImage.launchimage/Default@2x.png | Bin 0 -> 14035 bytes ChartsDemo/Resources/app-icon/Icon-29@2x.png | Bin 0 -> 5677 bytes ChartsDemo/Resources/app-icon/Icon-29@3x.png | Bin 0 -> 10210 bytes ChartsDemo/Resources/app-icon/Icon-40@2x.png | Bin 0 -> 9266 bytes ChartsDemo/Resources/app-icon/Icon-40@3x.png | Bin 0 -> 14343 bytes ChartsDemo/Resources/app-icon/Icon-60@2x.png | Bin 0 -> 14343 bytes ChartsDemo/Resources/app-icon/Icon-60@3x.png | Bin 0 -> 27502 bytes ChartsDemo/Resources/app-icon/iTunesArtwork | Bin 0 -> 127259 bytes .../Resources/app-icon/iTunesArtwork@2x | Bin 0 -> 333159 bytes .../launch-image/Default-568h@2x.png | Bin 0 -> 16567 bytes .../launch-image/Default-667h@2x.png | Bin 0 -> 22355 bytes .../launch-image/Default-736h@3x.png | Bin 0 -> 55285 bytes .../Resources/launch-image/Default@2x.png | Bin 0 -> 14035 bytes .../ChartsDemo-Bridging-Header.h | 4 + ChartsDemo/Supporting Files/Info.plist | 36 + ChartsDemo/Supporting Files/main.m | 22 + 155 files changed, 21267 insertions(+) create mode 100644 Charts/Charts.xcodeproj/project.pbxproj create mode 100644 Charts/Classes/Animation/ChartAnimator.swift create mode 100644 Charts/Classes/Charts/BarChartView.swift create mode 100644 Charts/Classes/Charts/BarLineChartViewBase.swift create mode 100644 Charts/Classes/Charts/CandleStickChartView.swift create mode 100644 Charts/Classes/Charts/ChartViewBase.swift create mode 100644 Charts/Classes/Charts/CombinedChartView.swift create mode 100644 Charts/Classes/Charts/HorizontalBarChartView.swift create mode 100644 Charts/Classes/Charts/LineChartView.swift create mode 100644 Charts/Classes/Charts/PieChartView.swift create mode 100644 Charts/Classes/Charts/PieRadarChartViewBase.swift create mode 100644 Charts/Classes/Charts/RadarChartView.swift create mode 100644 Charts/Classes/Charts/ScatterChartView.swift create mode 100644 Charts/Classes/Components/ChartAxisBase.swift create mode 100644 Charts/Classes/Components/ChartComponentBase.swift create mode 100644 Charts/Classes/Components/ChartLegend.swift create mode 100644 Charts/Classes/Components/ChartLimitLine.swift create mode 100644 Charts/Classes/Components/ChartMarker.swift create mode 100644 Charts/Classes/Components/ChartXAxis.swift create mode 100644 Charts/Classes/Components/ChartYAxis.swift create mode 100644 Charts/Classes/Data/BarChartData.swift create mode 100644 Charts/Classes/Data/BarChartDataEntry.swift create mode 100644 Charts/Classes/Data/BarChartDataSet.swift create mode 100644 Charts/Classes/Data/BarLineScatterCandleChartData.swift create mode 100644 Charts/Classes/Data/BarLineScatterCandleChartDataSet.swift create mode 100644 Charts/Classes/Data/CandleChartData.swift create mode 100644 Charts/Classes/Data/CandleChartDataEntry.swift create mode 100644 Charts/Classes/Data/CandleChartDataSet.swift create mode 100644 Charts/Classes/Data/ChartData.swift create mode 100644 Charts/Classes/Data/ChartDataEntry.swift create mode 100644 Charts/Classes/Data/ChartDataSet.swift create mode 100644 Charts/Classes/Data/CombinedChartData.swift create mode 100644 Charts/Classes/Data/LineChartData.swift create mode 100644 Charts/Classes/Data/LineChartDataSet.swift create mode 100644 Charts/Classes/Data/LineRadarChartDataSet.swift create mode 100644 Charts/Classes/Data/PieChartData.swift create mode 100644 Charts/Classes/Data/PieChartDataSet.swift create mode 100644 Charts/Classes/Data/RadarChartData.swift create mode 100644 Charts/Classes/Data/RadarChartDataSet.swift create mode 100644 Charts/Classes/Data/ScatterChartData.swift create mode 100644 Charts/Classes/Data/ScatterChartDataSet.swift create mode 100644 Charts/Classes/Filters/ChartDataApproximatorFilter.swift create mode 100644 Charts/Classes/Filters/ChartDataBaseFilter.swift create mode 100644 Charts/Classes/Renderers/BarChartRenderer.swift create mode 100644 Charts/Classes/Renderers/CandleStickChartRenderer.swift create mode 100644 Charts/Classes/Renderers/ChartAxisRendererBase.swift create mode 100644 Charts/Classes/Renderers/ChartDataRendererBase.swift create mode 100644 Charts/Classes/Renderers/ChartLegendRenderer.swift create mode 100644 Charts/Classes/Renderers/ChartRendererBase.swift create mode 100644 Charts/Classes/Renderers/ChartXAxisRenderer.swift create mode 100644 Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift create mode 100644 Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift create mode 100644 Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift create mode 100644 Charts/Classes/Renderers/ChartYAxisRenderer.swift create mode 100644 Charts/Classes/Renderers/ChartYAxisRendererHorizontalBarChart.swift create mode 100644 Charts/Classes/Renderers/ChartYAxisRendererRadarChart.swift create mode 100644 Charts/Classes/Renderers/CombinedChartRenderer.swift create mode 100644 Charts/Classes/Renderers/HorizontalBarChartRenderer.swift create mode 100644 Charts/Classes/Renderers/LineChartRenderer.swift create mode 100644 Charts/Classes/Renderers/PieChartRenderer.swift create mode 100644 Charts/Classes/Renderers/RadarChartRenderer.swift create mode 100644 Charts/Classes/Renderers/ScatterChartRenderer.swift create mode 100644 Charts/Classes/Utils/ChartColorTemplates.swift create mode 100644 Charts/Classes/Utils/ChartFillFormatter.swift create mode 100644 Charts/Classes/Utils/ChartHighlight.swift create mode 100644 Charts/Classes/Utils/ChartSelInfo.swift create mode 100644 Charts/Classes/Utils/ChartTransformer.swift create mode 100644 Charts/Classes/Utils/ChartUtils.swift create mode 100644 Charts/Classes/Utils/ChartViewPortHandler.swift create mode 100755 Charts/Supporting Files/Charts.h create mode 100644 Charts/Supporting Files/Info.plist create mode 100644 ChartsDemo/ChartsDemo.xcodeproj/project.pbxproj create mode 100644 ChartsDemo/Classes/AppDelegate.h create mode 100644 ChartsDemo/Classes/AppDelegate.m create mode 100644 ChartsDemo/Classes/Components/BalloonMarker.swift create mode 100644 ChartsDemo/Classes/DemoBaseViewController.h create mode 100644 ChartsDemo/Classes/DemoBaseViewController.m create mode 100644 ChartsDemo/Classes/DemoListViewController.h create mode 100644 ChartsDemo/Classes/DemoListViewController.m create mode 100644 ChartsDemo/Classes/DemoListViewController.xib create mode 100644 ChartsDemo/Classes/Demos/AnotherBarChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/AnotherBarChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/AnotherBarChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/BarChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/BarChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/BarChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/CandleStickChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/CandleStickChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/CandleStickChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/ColoredLineChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/ColoredLineChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/ColoredLineChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/CombinedChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/CombinedChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/CombinedChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/CubicLineChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/CubicLineChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/CubicLineChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/HorizontalBarChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/HorizontalBarChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/HorizontalBarChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/LineChart1ViewController.h create mode 100644 ChartsDemo/Classes/Demos/LineChart1ViewController.m create mode 100644 ChartsDemo/Classes/Demos/LineChart1ViewController.xib create mode 100644 ChartsDemo/Classes/Demos/LineChart2ViewController.h create mode 100644 ChartsDemo/Classes/Demos/LineChart2ViewController.m create mode 100644 ChartsDemo/Classes/Demos/LineChart2ViewController.xib create mode 100644 ChartsDemo/Classes/Demos/MultipleBarChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/MultipleBarChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/MultipleBarChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/MultipleLinesChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/MultipleLinesChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/MultipleLinesChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/PieChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/PieChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/PieChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/RadarChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/RadarChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/RadarChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/ScatterChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/ScatterChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/ScatterChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/SinusBarChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/SinusBarChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/SinusBarChartViewController.xib create mode 100644 ChartsDemo/Classes/Demos/StackedBarChartViewController.h create mode 100644 ChartsDemo/Classes/Demos/StackedBarChartViewController.m create mode 100644 ChartsDemo/Classes/Demos/StackedBarChartViewController.xib create mode 100644 ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@2x.png create mode 100644 ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@3x.png create mode 100644 ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png create mode 100644 ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png create mode 100644 ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png create mode 100644 ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png create mode 100644 ChartsDemo/Resources/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 ChartsDemo/Resources/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png create mode 100644 ChartsDemo/Resources/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png create mode 100644 ChartsDemo/Resources/Images.xcassets/LaunchImage.launchimage/Default-736h@3x.png create mode 100644 ChartsDemo/Resources/Images.xcassets/LaunchImage.launchimage/Default@2x.png create mode 100644 ChartsDemo/Resources/app-icon/Icon-29@2x.png create mode 100644 ChartsDemo/Resources/app-icon/Icon-29@3x.png create mode 100644 ChartsDemo/Resources/app-icon/Icon-40@2x.png create mode 100644 ChartsDemo/Resources/app-icon/Icon-40@3x.png create mode 100644 ChartsDemo/Resources/app-icon/Icon-60@2x.png create mode 100644 ChartsDemo/Resources/app-icon/Icon-60@3x.png create mode 100644 ChartsDemo/Resources/app-icon/iTunesArtwork create mode 100644 ChartsDemo/Resources/app-icon/iTunesArtwork@2x create mode 100755 ChartsDemo/Resources/launch-image/Default-568h@2x.png create mode 100755 ChartsDemo/Resources/launch-image/Default-667h@2x.png create mode 100755 ChartsDemo/Resources/launch-image/Default-736h@3x.png create mode 100755 ChartsDemo/Resources/launch-image/Default@2x.png create mode 100644 ChartsDemo/Supporting Files/ChartsDemo-Bridging-Header.h create mode 100644 ChartsDemo/Supporting Files/Info.plist create mode 100644 ChartsDemo/Supporting Files/main.m diff --git a/Charts/Charts.xcodeproj/project.pbxproj b/Charts/Charts.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..0e54da9c3e --- /dev/null +++ b/Charts/Charts.xcodeproj/project.pbxproj @@ -0,0 +1,626 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5B4BCD3E1AA9C0A60063F019 /* ChartFillFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4BCD3D1AA9C0A60063F019 /* ChartFillFormatter.swift */; }; + 5B4BCD401AA9C4930063F019 /* ChartTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4BCD3F1AA9C4930063F019 /* ChartTransformer.swift */; }; + 5B6556F71AB72BA000FFBFD3 /* ChartComponentBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6556F61AB72BA000FFBFD3 /* ChartComponentBase.swift */; }; + 5B680D1D1A9D16F90026A057 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B680D1C1A9D16F90026A057 /* UIKit.framework */; }; + 5B680D1F1A9D17C30026A057 /* ChartAxisBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC6A1A9D151C00CE82E1 /* ChartAxisBase.swift */; }; + 5B680D201A9D17C30026A057 /* ChartLegend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC6B1A9D151C00CE82E1 /* ChartLegend.swift */; }; + 5B680D211A9D17C30026A057 /* ChartLimitLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC6C1A9D151C00CE82E1 /* ChartLimitLine.swift */; }; + 5B680D221A9D17C30026A057 /* ChartXAxis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC6D1A9D151C00CE82E1 /* ChartXAxis.swift */; }; + 5B680D231A9D17C30026A057 /* ChartYAxis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC6E1A9D151C00CE82E1 /* ChartYAxis.swift */; }; + 5B680D271A9D17C30026A057 /* ChartColorTemplates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC791A9D151C00CE82E1 /* ChartColorTemplates.swift */; }; + 5B680D281A9D17C30026A057 /* ChartHighlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC7A1A9D151C00CE82E1 /* ChartHighlight.swift */; }; + 5B680D291A9D17C30026A057 /* ChartSelInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC7B1A9D151C00CE82E1 /* ChartSelInfo.swift */; }; + 5B680D2A1A9D17C30026A057 /* ChartUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC7C1A9D151C00CE82E1 /* ChartUtils.swift */; }; + 5B680D3D1A9D1AD90026A057 /* Charts.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B680D3C1A9D1AD90026A057 /* Charts.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5B6A546B1AA5C23F000F57C2 /* ChartMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A546A1AA5C23F000F57C2 /* ChartMarker.swift */; }; + 5B6A546E1AA5D2DC000F57C2 /* ChartAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A546D1AA5D2DC000F57C2 /* ChartAnimator.swift */; }; + 5B6A54701AA5DB34000F57C2 /* ChartRendererBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A546F1AA5DB34000F57C2 /* ChartRendererBase.swift */; }; + 5B6A54741AA5DEDC000F57C2 /* ChartXAxisRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54731AA5DEDC000F57C2 /* ChartXAxisRenderer.swift */; }; + 5B6A54761AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54751AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift */; }; + 5B6A54781AA5DEF0000F57C2 /* ChartXAxisRendererRadarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54771AA5DEF0000F57C2 /* ChartXAxisRendererRadarChart.swift */; }; + 5B6A547C1AA5DF02000F57C2 /* ChartXAxisRendererHorizontalBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A547B1AA5DF02000F57C2 /* ChartXAxisRendererHorizontalBarChart.swift */; }; + 5B6A547E1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A547D1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift */; }; + 5B6A54801AA5DF28000F57C2 /* ChartYAxisRendererHorizontalBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A547F1AA5DF28000F57C2 /* ChartYAxisRendererHorizontalBarChart.swift */; }; + 5B6A54821AA5DF34000F57C2 /* ChartYAxisRendererRadarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54811AA5DF34000F57C2 /* ChartYAxisRendererRadarChart.swift */; }; + 5B6A54851AA669C9000F57C2 /* ScatterChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54841AA669C9000F57C2 /* ScatterChartRenderer.swift */; }; + 5B6A54871AA669F4000F57C2 /* RadarChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54861AA669F4000F57C2 /* RadarChartRenderer.swift */; }; + 5B6A54891AA66A1A000F57C2 /* PieChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54881AA66A1A000F57C2 /* PieChartRenderer.swift */; }; + 5B6A548B1AA66A3D000F57C2 /* LineChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A548A1AA66A3D000F57C2 /* LineChartRenderer.swift */; }; + 5B6A548D1AA66A60000F57C2 /* ChartLegendRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A548C1AA66A60000F57C2 /* ChartLegendRenderer.swift */; }; + 5B6A548F1AA66A7A000F57C2 /* HorizontalBarChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A548E1AA66A7A000F57C2 /* HorizontalBarChartRenderer.swift */; }; + 5B6A54911AA66A8D000F57C2 /* ChartDataRendererBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54901AA66A8D000F57C2 /* ChartDataRendererBase.swift */; }; + 5B6A54931AA66AAB000F57C2 /* CombinedChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54921AA66AAB000F57C2 /* CombinedChartRenderer.swift */; }; + 5B6A54951AA66AC0000F57C2 /* CandleStickChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54941AA66AC0000F57C2 /* CandleStickChartRenderer.swift */; }; + 5B6A54971AA66AD2000F57C2 /* BarChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54961AA66AD2000F57C2 /* BarChartRenderer.swift */; }; + 5B6A54991AA66B14000F57C2 /* BarChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54981AA66B14000F57C2 /* BarChartView.swift */; }; + 5B6A549B1AA66B2C000F57C2 /* BarLineChartViewBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A549A1AA66B2C000F57C2 /* BarLineChartViewBase.swift */; }; + 5B6A549D1AA66B3C000F57C2 /* CandleStickChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A549C1AA66B3C000F57C2 /* CandleStickChartView.swift */; }; + 5B6A549F1AA66B59000F57C2 /* CombinedChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A549E1AA66B59000F57C2 /* CombinedChartView.swift */; }; + 5B6A54A31AA66B7C000F57C2 /* LineChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54A21AA66B7C000F57C2 /* LineChartView.swift */; }; + 5B6A54A51AA66B92000F57C2 /* PieChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54A41AA66B92000F57C2 /* PieChartView.swift */; }; + 5B6A54A71AA66BA7000F57C2 /* PieRadarChartViewBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54A61AA66BA7000F57C2 /* PieRadarChartViewBase.swift */; }; + 5B6A54A91AA66BBA000F57C2 /* RadarChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54A81AA66BBA000F57C2 /* RadarChartView.swift */; }; + 5B6A54AB1AA66BC8000F57C2 /* ScatterChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54AA1AA66BC8000F57C2 /* ScatterChartView.swift */; }; + 5B6A54AC1AA66C1E000F57C2 /* ChartAxisRendererBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54711AA5DCA8000F57C2 /* ChartAxisRendererBase.swift */; }; + 5B6A54CC1AA74516000F57C2 /* BarChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54B71AA74516000F57C2 /* BarChartData.swift */; }; + 5B6A54CD1AA74516000F57C2 /* BarChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54B81AA74516000F57C2 /* BarChartDataEntry.swift */; }; + 5B6A54CE1AA74516000F57C2 /* BarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54B91AA74516000F57C2 /* BarChartDataSet.swift */; }; + 5B6A54CF1AA74516000F57C2 /* BarLineScatterCandleChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54BA1AA74516000F57C2 /* BarLineScatterCandleChartData.swift */; }; + 5B6A54D01AA74516000F57C2 /* BarLineScatterCandleChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54BB1AA74516000F57C2 /* BarLineScatterCandleChartDataSet.swift */; }; + 5B6A54D11AA74516000F57C2 /* CandleChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54BC1AA74516000F57C2 /* CandleChartData.swift */; }; + 5B6A54D21AA74516000F57C2 /* CandleChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54BD1AA74516000F57C2 /* CandleChartDataEntry.swift */; }; + 5B6A54D31AA74516000F57C2 /* CandleChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54BE1AA74516000F57C2 /* CandleChartDataSet.swift */; }; + 5B6A54D41AA74516000F57C2 /* CombinedChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54BF1AA74516000F57C2 /* CombinedChartData.swift */; }; + 5B6A54D51AA74516000F57C2 /* ChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54C01AA74516000F57C2 /* ChartData.swift */; }; + 5B6A54D61AA74516000F57C2 /* ChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54C11AA74516000F57C2 /* ChartDataEntry.swift */; }; + 5B6A54D71AA74516000F57C2 /* ChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54C21AA74516000F57C2 /* ChartDataSet.swift */; }; + 5B6A54D81AA74516000F57C2 /* LineChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54C31AA74516000F57C2 /* LineChartData.swift */; }; + 5B6A54D91AA74516000F57C2 /* LineChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54C41AA74516000F57C2 /* LineChartDataSet.swift */; }; + 5B6A54DA1AA74516000F57C2 /* LineRadarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54C51AA74516000F57C2 /* LineRadarChartDataSet.swift */; }; + 5B6A54DB1AA74516000F57C2 /* PieChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54C61AA74516000F57C2 /* PieChartData.swift */; }; + 5B6A54DC1AA74516000F57C2 /* PieChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54C71AA74516000F57C2 /* PieChartDataSet.swift */; }; + 5B6A54DD1AA74516000F57C2 /* RadarChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54C81AA74516000F57C2 /* RadarChartData.swift */; }; + 5B6A54DE1AA74516000F57C2 /* RadarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54C91AA74516000F57C2 /* RadarChartDataSet.swift */; }; + 5B6A54DF1AA74516000F57C2 /* ScatterChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54CA1AA74516000F57C2 /* ScatterChartData.swift */; }; + 5B6A54E01AA74516000F57C2 /* ScatterChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54CB1AA74516000F57C2 /* ScatterChartDataSet.swift */; }; + 5BA8EC7D1A9D151C00CE82E1 /* ChartViewBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC671A9D151C00CE82E1 /* ChartViewBase.swift */; }; + 5BA8EC881A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift */; }; + 5BA8EC891A9D151C00CE82E1 /* ChartDataBaseFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC761A9D151C00CE82E1 /* ChartDataBaseFilter.swift */; }; + 5BD8F06D1AB897D500566E05 /* ChartViewPortHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD8F06C1AB897D500566E05 /* ChartViewPortHandler.swift */; }; + 5BD8F06E1AB89AD800566E05 /* HorizontalBarChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54A01AA66B6A000F57C2 /* HorizontalBarChartView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 5B4BCD3D1AA9C0A60063F019 /* ChartFillFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartFillFormatter.swift; sourceTree = ""; }; + 5B4BCD3F1AA9C4930063F019 /* ChartTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartTransformer.swift; sourceTree = ""; }; + 5B6556F61AB72BA000FFBFD3 /* ChartComponentBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartComponentBase.swift; sourceTree = ""; }; + 5B680D1C1A9D16F90026A057 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 5B680D3C1A9D1AD90026A057 /* Charts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Charts.h; sourceTree = ""; }; + 5B6A546A1AA5C23F000F57C2 /* ChartMarker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartMarker.swift; sourceTree = ""; }; + 5B6A546D1AA5D2DC000F57C2 /* ChartAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartAnimator.swift; sourceTree = ""; }; + 5B6A546F1AA5DB34000F57C2 /* ChartRendererBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartRendererBase.swift; sourceTree = ""; }; + 5B6A54711AA5DCA8000F57C2 /* ChartAxisRendererBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartAxisRendererBase.swift; sourceTree = ""; }; + 5B6A54731AA5DEDC000F57C2 /* ChartXAxisRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxisRenderer.swift; sourceTree = ""; }; + 5B6A54751AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxisRendererBarChart.swift; sourceTree = ""; }; + 5B6A54771AA5DEF0000F57C2 /* ChartXAxisRendererRadarChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxisRendererRadarChart.swift; sourceTree = ""; }; + 5B6A547B1AA5DF02000F57C2 /* ChartXAxisRendererHorizontalBarChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxisRendererHorizontalBarChart.swift; sourceTree = ""; }; + 5B6A547D1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartYAxisRenderer.swift; sourceTree = ""; }; + 5B6A547F1AA5DF28000F57C2 /* ChartYAxisRendererHorizontalBarChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartYAxisRendererHorizontalBarChart.swift; sourceTree = ""; }; + 5B6A54811AA5DF34000F57C2 /* ChartYAxisRendererRadarChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartYAxisRendererRadarChart.swift; sourceTree = ""; }; + 5B6A54841AA669C9000F57C2 /* ScatterChartRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScatterChartRenderer.swift; sourceTree = ""; }; + 5B6A54861AA669F4000F57C2 /* RadarChartRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadarChartRenderer.swift; sourceTree = ""; }; + 5B6A54881AA66A1A000F57C2 /* PieChartRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PieChartRenderer.swift; sourceTree = ""; }; + 5B6A548A1AA66A3D000F57C2 /* LineChartRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineChartRenderer.swift; sourceTree = ""; }; + 5B6A548C1AA66A60000F57C2 /* ChartLegendRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartLegendRenderer.swift; sourceTree = ""; }; + 5B6A548E1AA66A7A000F57C2 /* HorizontalBarChartRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalBarChartRenderer.swift; sourceTree = ""; }; + 5B6A54901AA66A8D000F57C2 /* ChartDataRendererBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDataRendererBase.swift; sourceTree = ""; }; + 5B6A54921AA66AAB000F57C2 /* CombinedChartRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombinedChartRenderer.swift; sourceTree = ""; }; + 5B6A54941AA66AC0000F57C2 /* CandleStickChartRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CandleStickChartRenderer.swift; sourceTree = ""; }; + 5B6A54961AA66AD2000F57C2 /* BarChartRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartRenderer.swift; sourceTree = ""; }; + 5B6A54981AA66B14000F57C2 /* BarChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartView.swift; sourceTree = ""; }; + 5B6A549A1AA66B2C000F57C2 /* BarLineChartViewBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = BarLineChartViewBase.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 5B6A549C1AA66B3C000F57C2 /* CandleStickChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CandleStickChartView.swift; sourceTree = ""; }; + 5B6A549E1AA66B59000F57C2 /* CombinedChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombinedChartView.swift; sourceTree = ""; }; + 5B6A54A01AA66B6A000F57C2 /* HorizontalBarChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalBarChartView.swift; sourceTree = ""; }; + 5B6A54A21AA66B7C000F57C2 /* LineChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineChartView.swift; sourceTree = ""; }; + 5B6A54A41AA66B92000F57C2 /* PieChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PieChartView.swift; sourceTree = ""; }; + 5B6A54A61AA66BA7000F57C2 /* PieRadarChartViewBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PieRadarChartViewBase.swift; sourceTree = ""; }; + 5B6A54A81AA66BBA000F57C2 /* RadarChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadarChartView.swift; sourceTree = ""; }; + 5B6A54AA1AA66BC8000F57C2 /* ScatterChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScatterChartView.swift; sourceTree = ""; }; + 5B6A54B71AA74516000F57C2 /* BarChartData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartData.swift; sourceTree = ""; }; + 5B6A54B81AA74516000F57C2 /* BarChartDataEntry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartDataEntry.swift; sourceTree = ""; }; + 5B6A54B91AA74516000F57C2 /* BarChartDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartDataSet.swift; sourceTree = ""; }; + 5B6A54BA1AA74516000F57C2 /* BarLineScatterCandleChartData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarLineScatterCandleChartData.swift; sourceTree = ""; }; + 5B6A54BB1AA74516000F57C2 /* BarLineScatterCandleChartDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarLineScatterCandleChartDataSet.swift; sourceTree = ""; }; + 5B6A54BC1AA74516000F57C2 /* CandleChartData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CandleChartData.swift; sourceTree = ""; }; + 5B6A54BD1AA74516000F57C2 /* CandleChartDataEntry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CandleChartDataEntry.swift; sourceTree = ""; }; + 5B6A54BE1AA74516000F57C2 /* CandleChartDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CandleChartDataSet.swift; sourceTree = ""; }; + 5B6A54BF1AA74516000F57C2 /* CombinedChartData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombinedChartData.swift; sourceTree = ""; }; + 5B6A54C01AA74516000F57C2 /* ChartData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartData.swift; sourceTree = ""; }; + 5B6A54C11AA74516000F57C2 /* ChartDataEntry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDataEntry.swift; sourceTree = ""; }; + 5B6A54C21AA74516000F57C2 /* ChartDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDataSet.swift; sourceTree = ""; }; + 5B6A54C31AA74516000F57C2 /* LineChartData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineChartData.swift; sourceTree = ""; }; + 5B6A54C41AA74516000F57C2 /* LineChartDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineChartDataSet.swift; sourceTree = ""; }; + 5B6A54C51AA74516000F57C2 /* LineRadarChartDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineRadarChartDataSet.swift; sourceTree = ""; }; + 5B6A54C61AA74516000F57C2 /* PieChartData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PieChartData.swift; sourceTree = ""; }; + 5B6A54C71AA74516000F57C2 /* PieChartDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PieChartDataSet.swift; sourceTree = ""; }; + 5B6A54C81AA74516000F57C2 /* RadarChartData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadarChartData.swift; sourceTree = ""; }; + 5B6A54C91AA74516000F57C2 /* RadarChartDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadarChartDataSet.swift; sourceTree = ""; }; + 5B6A54CA1AA74516000F57C2 /* ScatterChartData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScatterChartData.swift; sourceTree = ""; }; + 5B6A54CB1AA74516000F57C2 /* ScatterChartDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScatterChartDataSet.swift; sourceTree = ""; }; + 5BA8EC401A9D14DC00CE82E1 /* Charts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Charts.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5BA8EC441A9D14DC00CE82E1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5BA8EC671A9D151C00CE82E1 /* ChartViewBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartViewBase.swift; sourceTree = ""; }; + 5BA8EC6A1A9D151C00CE82E1 /* ChartAxisBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ChartAxisBase.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 5BA8EC6B1A9D151C00CE82E1 /* ChartLegend.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartLegend.swift; sourceTree = ""; }; + 5BA8EC6C1A9D151C00CE82E1 /* ChartLimitLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartLimitLine.swift; sourceTree = ""; }; + 5BA8EC6D1A9D151C00CE82E1 /* ChartXAxis.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxis.swift; sourceTree = ""; }; + 5BA8EC6E1A9D151C00CE82E1 /* ChartYAxis.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartYAxis.swift; sourceTree = ""; }; + 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDataApproximatorFilter.swift; sourceTree = ""; }; + 5BA8EC761A9D151C00CE82E1 /* ChartDataBaseFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDataBaseFilter.swift; sourceTree = ""; }; + 5BA8EC791A9D151C00CE82E1 /* ChartColorTemplates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartColorTemplates.swift; sourceTree = ""; }; + 5BA8EC7A1A9D151C00CE82E1 /* ChartHighlight.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartHighlight.swift; sourceTree = ""; }; + 5BA8EC7B1A9D151C00CE82E1 /* ChartSelInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartSelInfo.swift; sourceTree = ""; }; + 5BA8EC7C1A9D151C00CE82E1 /* ChartUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartUtils.swift; sourceTree = ""; }; + 5BD8F06C1AB897D500566E05 /* ChartViewPortHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartViewPortHandler.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5BA8EC3C1A9D14DC00CE82E1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5B680D1D1A9D16F90026A057 /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5B680D1E1A9D170B0026A057 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 5B680D1C1A9D16F90026A057 /* UIKit.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5B6A546C1AA5D2D0000F57C2 /* Animation */ = { + isa = PBXGroup; + children = ( + 5B6A546D1AA5D2DC000F57C2 /* ChartAnimator.swift */, + ); + path = Animation; + sourceTree = ""; + }; + 5B759ED41A9F98A90039D97F /* Renderers */ = { + isa = PBXGroup; + children = ( + 5B6A54711AA5DCA8000F57C2 /* ChartAxisRendererBase.swift */, + 5B6A54961AA66AD2000F57C2 /* BarChartRenderer.swift */, + 5B6A54941AA66AC0000F57C2 /* CandleStickChartRenderer.swift */, + 5B6A54921AA66AAB000F57C2 /* CombinedChartRenderer.swift */, + 5B6A54901AA66A8D000F57C2 /* ChartDataRendererBase.swift */, + 5B6A548E1AA66A7A000F57C2 /* HorizontalBarChartRenderer.swift */, + 5B6A548C1AA66A60000F57C2 /* ChartLegendRenderer.swift */, + 5B6A548A1AA66A3D000F57C2 /* LineChartRenderer.swift */, + 5B6A54881AA66A1A000F57C2 /* PieChartRenderer.swift */, + 5B6A54861AA669F4000F57C2 /* RadarChartRenderer.swift */, + 5B6A546F1AA5DB34000F57C2 /* ChartRendererBase.swift */, + 5B6A54841AA669C9000F57C2 /* ScatterChartRenderer.swift */, + 5B6A54731AA5DEDC000F57C2 /* ChartXAxisRenderer.swift */, + 5B6A54751AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift */, + 5B6A547B1AA5DF02000F57C2 /* ChartXAxisRendererHorizontalBarChart.swift */, + 5B6A54771AA5DEF0000F57C2 /* ChartXAxisRendererRadarChart.swift */, + 5B6A547D1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift */, + 5B6A547F1AA5DF28000F57C2 /* ChartYAxisRendererHorizontalBarChart.swift */, + 5B6A54811AA5DF34000F57C2 /* ChartYAxisRendererRadarChart.swift */, + ); + path = Renderers; + sourceTree = ""; + }; + 5BA8EC361A9D14DC00CE82E1 = { + isa = PBXGroup; + children = ( + 5BA8EC651A9D151C00CE82E1 /* Classes */, + 5BA8EC431A9D14DC00CE82E1 /* Supporting Files */, + 5BA8EC411A9D14DC00CE82E1 /* Products */, + 5B680D1E1A9D170B0026A057 /* Frameworks */, + ); + sourceTree = ""; + }; + 5BA8EC411A9D14DC00CE82E1 /* Products */ = { + isa = PBXGroup; + children = ( + 5BA8EC401A9D14DC00CE82E1 /* Charts.framework */, + ); + name = Products; + sourceTree = ""; + }; + 5BA8EC431A9D14DC00CE82E1 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 5B680D3C1A9D1AD90026A057 /* Charts.h */, + 5BA8EC441A9D14DC00CE82E1 /* Info.plist */, + ); + path = "Supporting Files"; + sourceTree = ""; + }; + 5BA8EC651A9D151C00CE82E1 /* Classes */ = { + isa = PBXGroup; + children = ( + 5B6A546C1AA5D2D0000F57C2 /* Animation */, + 5BA8EC661A9D151C00CE82E1 /* Charts */, + 5BA8EC691A9D151C00CE82E1 /* Components */, + 5BA8EC6F1A9D151C00CE82E1 /* Data */, + 5BA8EC741A9D151C00CE82E1 /* Filters */, + 5B759ED41A9F98A90039D97F /* Renderers */, + 5BA8EC781A9D151C00CE82E1 /* Utils */, + ); + path = Classes; + sourceTree = ""; + }; + 5BA8EC661A9D151C00CE82E1 /* Charts */ = { + isa = PBXGroup; + children = ( + 5B6A54981AA66B14000F57C2 /* BarChartView.swift */, + 5B6A549A1AA66B2C000F57C2 /* BarLineChartViewBase.swift */, + 5B6A549C1AA66B3C000F57C2 /* CandleStickChartView.swift */, + 5BA8EC671A9D151C00CE82E1 /* ChartViewBase.swift */, + 5B6A549E1AA66B59000F57C2 /* CombinedChartView.swift */, + 5B6A54A01AA66B6A000F57C2 /* HorizontalBarChartView.swift */, + 5B6A54A21AA66B7C000F57C2 /* LineChartView.swift */, + 5B6A54A41AA66B92000F57C2 /* PieChartView.swift */, + 5B6A54A61AA66BA7000F57C2 /* PieRadarChartViewBase.swift */, + 5B6A54A81AA66BBA000F57C2 /* RadarChartView.swift */, + 5B6A54AA1AA66BC8000F57C2 /* ScatterChartView.swift */, + ); + path = Charts; + sourceTree = ""; + }; + 5BA8EC691A9D151C00CE82E1 /* Components */ = { + isa = PBXGroup; + children = ( + 5BA8EC6A1A9D151C00CE82E1 /* ChartAxisBase.swift */, + 5B6556F61AB72BA000FFBFD3 /* ChartComponentBase.swift */, + 5BA8EC6B1A9D151C00CE82E1 /* ChartLegend.swift */, + 5BA8EC6C1A9D151C00CE82E1 /* ChartLimitLine.swift */, + 5B6A546A1AA5C23F000F57C2 /* ChartMarker.swift */, + 5BA8EC6D1A9D151C00CE82E1 /* ChartXAxis.swift */, + 5BA8EC6E1A9D151C00CE82E1 /* ChartYAxis.swift */, + ); + path = Components; + sourceTree = ""; + }; + 5BA8EC6F1A9D151C00CE82E1 /* Data */ = { + isa = PBXGroup; + children = ( + 5B6A54B71AA74516000F57C2 /* BarChartData.swift */, + 5B6A54B81AA74516000F57C2 /* BarChartDataEntry.swift */, + 5B6A54B91AA74516000F57C2 /* BarChartDataSet.swift */, + 5B6A54BA1AA74516000F57C2 /* BarLineScatterCandleChartData.swift */, + 5B6A54BB1AA74516000F57C2 /* BarLineScatterCandleChartDataSet.swift */, + 5B6A54BC1AA74516000F57C2 /* CandleChartData.swift */, + 5B6A54BD1AA74516000F57C2 /* CandleChartDataEntry.swift */, + 5B6A54BE1AA74516000F57C2 /* CandleChartDataSet.swift */, + 5B6A54BF1AA74516000F57C2 /* CombinedChartData.swift */, + 5B6A54C01AA74516000F57C2 /* ChartData.swift */, + 5B6A54C11AA74516000F57C2 /* ChartDataEntry.swift */, + 5B6A54C21AA74516000F57C2 /* ChartDataSet.swift */, + 5B6A54C31AA74516000F57C2 /* LineChartData.swift */, + 5B6A54C41AA74516000F57C2 /* LineChartDataSet.swift */, + 5B6A54C51AA74516000F57C2 /* LineRadarChartDataSet.swift */, + 5B6A54C61AA74516000F57C2 /* PieChartData.swift */, + 5B6A54C71AA74516000F57C2 /* PieChartDataSet.swift */, + 5B6A54C81AA74516000F57C2 /* RadarChartData.swift */, + 5B6A54C91AA74516000F57C2 /* RadarChartDataSet.swift */, + 5B6A54CA1AA74516000F57C2 /* ScatterChartData.swift */, + 5B6A54CB1AA74516000F57C2 /* ScatterChartDataSet.swift */, + ); + path = Data; + sourceTree = ""; + }; + 5BA8EC741A9D151C00CE82E1 /* Filters */ = { + isa = PBXGroup; + children = ( + 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift */, + 5BA8EC761A9D151C00CE82E1 /* ChartDataBaseFilter.swift */, + ); + path = Filters; + sourceTree = ""; + }; + 5BA8EC781A9D151C00CE82E1 /* Utils */ = { + isa = PBXGroup; + children = ( + 5BA8EC791A9D151C00CE82E1 /* ChartColorTemplates.swift */, + 5B4BCD3D1AA9C0A60063F019 /* ChartFillFormatter.swift */, + 5BA8EC7A1A9D151C00CE82E1 /* ChartHighlight.swift */, + 5BA8EC7B1A9D151C00CE82E1 /* ChartSelInfo.swift */, + 5B4BCD3F1AA9C4930063F019 /* ChartTransformer.swift */, + 5BA8EC7C1A9D151C00CE82E1 /* ChartUtils.swift */, + 5BD8F06C1AB897D500566E05 /* ChartViewPortHandler.swift */, + ); + path = Utils; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 5BA8EC3D1A9D14DC00CE82E1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5B680D3D1A9D1AD90026A057 /* Charts.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 5BA8EC3F1A9D14DC00CE82E1 /* Charts */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5BA8EC561A9D14DC00CE82E1 /* Build configuration list for PBXNativeTarget "Charts" */; + buildPhases = ( + 5BA8EC3B1A9D14DC00CE82E1 /* Sources */, + 5BA8EC3C1A9D14DC00CE82E1 /* Frameworks */, + 5BA8EC3D1A9D14DC00CE82E1 /* Headers */, + 5BA8EC3E1A9D14DC00CE82E1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Charts; + productName = Charts; + productReference = 5BA8EC401A9D14DC00CE82E1 /* Charts.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5BA8EC371A9D14DC00CE82E1 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = dcg; + TargetAttributes = { + 5BA8EC3F1A9D14DC00CE82E1 = { + CreatedOnToolsVersion = 6.1.1; + }; + }; + }; + buildConfigurationList = 5BA8EC3A1A9D14DC00CE82E1 /* Build configuration list for PBXProject "Charts" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 5BA8EC361A9D14DC00CE82E1; + productRefGroup = 5BA8EC411A9D14DC00CE82E1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5BA8EC3F1A9D14DC00CE82E1 /* Charts */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 5BA8EC3E1A9D14DC00CE82E1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5BA8EC3B1A9D14DC00CE82E1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5B680D1F1A9D17C30026A057 /* ChartAxisBase.swift in Sources */, + 5B680D281A9D17C30026A057 /* ChartHighlight.swift in Sources */, + 5B4BCD3E1AA9C0A60063F019 /* ChartFillFormatter.swift in Sources */, + 5B6A54DE1AA74516000F57C2 /* RadarChartDataSet.swift in Sources */, + 5B680D211A9D17C30026A057 /* ChartLimitLine.swift in Sources */, + 5B6A54DB1AA74516000F57C2 /* PieChartData.swift in Sources */, + 5B6A54D91AA74516000F57C2 /* LineChartDataSet.swift in Sources */, + 5B6A54AC1AA66C1E000F57C2 /* ChartAxisRendererBase.swift in Sources */, + 5B6A54CF1AA74516000F57C2 /* BarLineScatterCandleChartData.swift in Sources */, + 5B6A54D11AA74516000F57C2 /* CandleChartData.swift in Sources */, + 5B6A54AB1AA66BC8000F57C2 /* ScatterChartView.swift in Sources */, + 5B6A54E01AA74516000F57C2 /* ScatterChartDataSet.swift in Sources */, + 5B6A548B1AA66A3D000F57C2 /* LineChartRenderer.swift in Sources */, + 5B6A54D01AA74516000F57C2 /* BarLineScatterCandleChartDataSet.swift in Sources */, + 5B6A54821AA5DF34000F57C2 /* ChartYAxisRendererRadarChart.swift in Sources */, + 5B6A54931AA66AAB000F57C2 /* CombinedChartRenderer.swift in Sources */, + 5B680D221A9D17C30026A057 /* ChartXAxis.swift in Sources */, + 5BA8EC891A9D151C00CE82E1 /* ChartDataBaseFilter.swift in Sources */, + 5B6A54A31AA66B7C000F57C2 /* LineChartView.swift in Sources */, + 5B6A54891AA66A1A000F57C2 /* PieChartRenderer.swift in Sources */, + 5B6A54DD1AA74516000F57C2 /* RadarChartData.swift in Sources */, + 5B6A54991AA66B14000F57C2 /* BarChartView.swift in Sources */, + 5B680D231A9D17C30026A057 /* ChartYAxis.swift in Sources */, + 5B6A54A91AA66BBA000F57C2 /* RadarChartView.swift in Sources */, + 5B6A548F1AA66A7A000F57C2 /* HorizontalBarChartRenderer.swift in Sources */, + 5B6A54741AA5DEDC000F57C2 /* ChartXAxisRenderer.swift in Sources */, + 5B6A547C1AA5DF02000F57C2 /* ChartXAxisRendererHorizontalBarChart.swift in Sources */, + 5B4BCD401AA9C4930063F019 /* ChartTransformer.swift in Sources */, + 5B6A54801AA5DF28000F57C2 /* ChartYAxisRendererHorizontalBarChart.swift in Sources */, + 5B6A54D21AA74516000F57C2 /* CandleChartDataEntry.swift in Sources */, + 5B6A54CC1AA74516000F57C2 /* BarChartData.swift in Sources */, + 5B6A54CE1AA74516000F57C2 /* BarChartDataSet.swift in Sources */, + 5B6A54871AA669F4000F57C2 /* RadarChartRenderer.swift in Sources */, + 5B6A548D1AA66A60000F57C2 /* ChartLegendRenderer.swift in Sources */, + 5B680D271A9D17C30026A057 /* ChartColorTemplates.swift in Sources */, + 5B6A54951AA66AC0000F57C2 /* CandleStickChartRenderer.swift in Sources */, + 5B680D291A9D17C30026A057 /* ChartSelInfo.swift in Sources */, + 5BA8EC7D1A9D151C00CE82E1 /* ChartViewBase.swift in Sources */, + 5B6A54DC1AA74516000F57C2 /* PieChartDataSet.swift in Sources */, + 5B6A54DA1AA74516000F57C2 /* LineRadarChartDataSet.swift in Sources */, + 5B6A54701AA5DB34000F57C2 /* ChartRendererBase.swift in Sources */, + 5B6A54761AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift in Sources */, + 5B6A54851AA669C9000F57C2 /* ScatterChartRenderer.swift in Sources */, + 5B6A549D1AA66B3C000F57C2 /* CandleStickChartView.swift in Sources */, + 5BA8EC881A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift in Sources */, + 5B6A549B1AA66B2C000F57C2 /* BarLineChartViewBase.swift in Sources */, + 5B6A54A51AA66B92000F57C2 /* PieChartView.swift in Sources */, + 5B6A54D81AA74516000F57C2 /* LineChartData.swift in Sources */, + 5B6A54911AA66A8D000F57C2 /* ChartDataRendererBase.swift in Sources */, + 5BD8F06D1AB897D500566E05 /* ChartViewPortHandler.swift in Sources */, + 5B6A54D51AA74516000F57C2 /* ChartData.swift in Sources */, + 5B6A54971AA66AD2000F57C2 /* BarChartRenderer.swift in Sources */, + 5B6A546B1AA5C23F000F57C2 /* ChartMarker.swift in Sources */, + 5B6A54D61AA74516000F57C2 /* ChartDataEntry.swift in Sources */, + 5B6A54DF1AA74516000F57C2 /* ScatterChartData.swift in Sources */, + 5B6A54D31AA74516000F57C2 /* CandleChartDataSet.swift in Sources */, + 5B6A54D71AA74516000F57C2 /* ChartDataSet.swift in Sources */, + 5B6A54781AA5DEF0000F57C2 /* ChartXAxisRendererRadarChart.swift in Sources */, + 5B6A54A71AA66BA7000F57C2 /* PieRadarChartViewBase.swift in Sources */, + 5B6A546E1AA5D2DC000F57C2 /* ChartAnimator.swift in Sources */, + 5B6A547E1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift in Sources */, + 5B6A549F1AA66B59000F57C2 /* CombinedChartView.swift in Sources */, + 5B6556F71AB72BA000FFBFD3 /* ChartComponentBase.swift in Sources */, + 5B6A54CD1AA74516000F57C2 /* BarChartDataEntry.swift in Sources */, + 5B6A54D41AA74516000F57C2 /* CombinedChartData.swift in Sources */, + 5B680D2A1A9D17C30026A057 /* ChartUtils.swift in Sources */, + 5B680D201A9D17C30026A057 /* ChartLegend.swift in Sources */, + 5BD8F06E1AB89AD800566E05 /* HorizontalBarChartView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 5BA8EC541A9D14DC00CE82E1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 5BA8EC551A9D14DC00CE82E1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 1; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 5BA8EC571A9D14DC00CE82E1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Supporting Files/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 5BA8EC581A9D14DC00CE82E1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Supporting Files/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 5BA8EC3A1A9D14DC00CE82E1 /* Build configuration list for PBXProject "Charts" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5BA8EC541A9D14DC00CE82E1 /* Debug */, + 5BA8EC551A9D14DC00CE82E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5BA8EC561A9D14DC00CE82E1 /* Build configuration list for PBXNativeTarget "Charts" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5BA8EC571A9D14DC00CE82E1 /* Debug */, + 5BA8EC581A9D14DC00CE82E1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5BA8EC371A9D14DC00CE82E1 /* Project object */; +} diff --git a/Charts/Classes/Animation/ChartAnimator.swift b/Charts/Classes/Animation/ChartAnimator.swift new file mode 100644 index 0000000000..6f3fad773d --- /dev/null +++ b/Charts/Classes/Animation/ChartAnimator.swift @@ -0,0 +1,149 @@ +// +// ChartAnimator.swift +// Charts +// +// Created by Daniel Cohen Gindi on 3/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit + +@objc +public protocol ChartAnimatorDelegate +{ + // Called when the Animator has stepped. + func chartAnimatorUpdated(chartAnimator: ChartAnimator); +} + +public class ChartAnimator: NSObject +{ + public weak var delegate: ChartAnimatorDelegate?; + + /// the phase that is animated and influences the drawn values on the y-axis + public var phaseX: CGFloat = 1.0 + + /// the phase that is animated and influences the drawn values on the y-axis + public var phaseY: CGFloat = 1.0 + + private var _startTime: NSTimeInterval = 0.0 + private var _displayLink: CADisplayLink! + private var _endTimeX: NSTimeInterval = 0.0 + private var _endTimeY: NSTimeInterval = 0.0 + private var _endTime: NSTimeInterval = 0.0 + private var _enabledX: Bool = false + private var _enabledY: Bool = false + + public override init() + { + super.init(); + } + + deinit + { + stop(); + } + + public func stop() + { + if (_displayLink != nil) + { + _displayLink.removeFromRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes); + _displayLink = nil; + + _enabledX = false; + _enabledY = false; + } + } + + @objc private func animationLoop() + { + var currentTime = CACurrentMediaTime(); + if (_enabledX) + { + var duration = _endTimeX - _startTime; + phaseX = duration == 0.0 ? 0.0 : CGFloat((currentTime - _startTime) / duration); + if (phaseX > 1.0) + { + phaseX = 1.0; + } + } + if (_enabledY) + { + var duration = _endTimeY - _startTime; + phaseY = duration == 0.0 ? 0.0 : CGFloat((currentTime - _startTime) / duration); + if (phaseY > 1.0) + { + phaseY = 1.0; + } + } + if (currentTime >= _endTime) + { + stop(); + } + if (delegate != nil) + { + delegate!.chartAnimatorUpdated(self); + } + } + + /// Animates the drawing / rendering of the chart on both x- and y-axis with + /// the specified animation time. + /// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart. + public func animateXY(#durationX: NSTimeInterval, durationY: NSTimeInterval) + { + stop(); + + _displayLink = CADisplayLink(target: self, selector: Selector("animationLoop")); + + _startTime = CACurrentMediaTime(); + _endTimeX = _startTime + durationX; + _endTimeY = _startTime + durationY; + _endTime = _endTimeX > _endTimeY ? _endTimeX : _endTimeY; + _enabledX = true; + _enabledY = true; + + _displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes); + } + + /// Animates the drawing / rendering of the chart the x-axis with + /// the specified animation time. + /// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart. + public func animateX(#duration: NSTimeInterval) + { + stop(); + + _displayLink = CADisplayLink(target: self, selector: Selector("animationLoop")); + + _startTime = CACurrentMediaTime(); + _endTimeX = _startTime + duration; + _endTime = _endTimeX; + _enabledX = true; + _enabledY = false; + + _displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes); + } + + /// Animates the drawing / rendering of the chart the y-axis with + /// the specified animation time. + /// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart. + public func animateY(#duration: NSTimeInterval) + { + stop(); + + _displayLink = CADisplayLink(target: self, selector: Selector("animationLoop")); + + _startTime = CACurrentMediaTime(); + _endTimeY = _startTime + duration; + _endTime = _endTimeY; + _enabledX = false; + _enabledY = true; + + _displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes); + } +} \ No newline at end of file diff --git a/Charts/Classes/Charts/BarChartView.swift b/Charts/Classes/Charts/BarChartView.swift new file mode 100644 index 0000000000..6befd6b844 --- /dev/null +++ b/Charts/Classes/Charts/BarChartView.swift @@ -0,0 +1,321 @@ +// +// BarChartView.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// Chart that draws bars. +public class BarChartView: BarLineChartViewBase, BarChartRendererDelegate +{ + /// flag that enables or disables the highlighting arrow + private var _drawHighlightArrowEnabled = false + + /// if set to true, all values are drawn above their bars, instead of below their top + private var _drawValueAboveBarEnabled = true + + /// if set to true, all values of a stack are drawn individually, and not just their sum + private var _drawValuesForWholeStackEnabled = true + + /// if set to true, a grey area is darawn behind each bar that indicates the maximum value + private var _drawBarShadowEnabled = false + + internal override func initialize() + { + super.initialize(); + + renderer = BarChartRenderer(delegate: self, animator: _animator, viewPortHandler: _viewPortHandler); + _xAxisRenderer = ChartXAxisRendererBarChart(viewPortHandler: _viewPortHandler, xAxis: _xAxis, transformer: _leftAxisTransformer, chart: self); + + _chartXMin = -0.5; + } + + internal override func calcMinMax() + { + super.calcMinMax(); + + if (_data === nil) + { + return; + } + + var barData = _data as! BarChartData; + + // increase deltax by 1 because the bars have a width of 1 + _deltaX += 0.5; + + // extend xDelta to make space for multiple datasets (if ther are one) + _deltaX *= CGFloat(_data.dataSetCount); + + var maxEntry = 0; + + for (var i = 0, count = barData.dataSetCount; i < count; i++) + { + var set = barData.getDataSetByIndex(i); + + if (maxEntry < set!.entryCount) + { + maxEntry = set!.entryCount; + } + } + + var groupSpace = barData.groupSpace; + _deltaX += CGFloat(maxEntry) * groupSpace; + _chartXMax = Float(_deltaX) - _chartXMin; + } + + /// Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point inside the BarChart. + public override func getHighlightByTouchPoint(var pt: CGPoint) -> ChartHighlight! + { + if (_dataNotSet || _data === nil) + { + println("Can't select by touch. No data set."); + return nil; + } + + _leftAxisTransformer.pixelToValue(&pt); + + if (pt.x < CGFloat(_chartXMin) || pt.x > CGFloat(_chartXMax)) + { + return nil; + } + + return getHighlight(xPosition: pt.x, yPosition: pt.y); + } + + /// Returns the correct Highlight object (including xIndex and dataSet-index) for the specified touch position. + internal func getHighlight(#xPosition: CGFloat, yPosition: CGFloat) -> ChartHighlight! + { + if (_dataNotSet || _data === nil) + { + return nil; + } + + var barData = _data as! BarChartData!; + + var setCount = barData.dataSetCount; + var valCount = barData.xValCount; + var dataSetIndex = 0; + var xIndex = 0; + + if (!barData.isGrouped) + { // only one dataset exists + + xIndex = Int(round(xPosition)); + + // check bounds + if (xIndex < 0) + { + xIndex = 0; + } + else if (xIndex >= valCount) + { + xIndex = valCount - 1; + } + } + else + { // if this bardata is grouped into more datasets + + // calculate how often the group-space appears + var steps = Int(xPosition / (CGFloat(setCount) + CGFloat(barData.groupSpace))); + + var groupSpaceSum = barData.groupSpace * CGFloat(steps); + + var baseNoSpace = xPosition - groupSpaceSum; + + dataSetIndex = Int(baseNoSpace) % setCount; + xIndex = Int(baseNoSpace) / setCount; + + // check bounds + if (xIndex < 0) + { + xIndex = 0; + dataSetIndex = 0; + } + else if (xIndex >= valCount) + { + xIndex = valCount - 1; + dataSetIndex = setCount - 1; + } + + // check bounds + if (dataSetIndex < 0) + { + dataSetIndex = 0; + } + else if (dataSetIndex >= setCount) + { + dataSetIndex = setCount - 1; + } + } + + var dataSet = barData.getDataSetByIndex(dataSetIndex) as! BarChartDataSet!; + if (!dataSet.isStacked) + { + return ChartHighlight(xIndex: xIndex, dataSetIndex: dataSetIndex); + } + else + { + return getStackedHighlight(xIndex: xIndex, dataSetIndex: dataSetIndex, yValue: Float(yPosition)); + } + } + + /// This method creates the Highlight object that also indicates which value of a stacked BarEntry has been selected. + internal func getStackedHighlight(#xIndex: Int, dataSetIndex: Int, yValue: Float) -> ChartHighlight! + { + var dataSet = _data.getDataSetByIndex(dataSetIndex); + var entry = dataSet.entryForXIndex(xIndex) as! BarChartDataEntry!; + + if (entry !== nil) + { + var stackIndex = entry.getClosestIndexAbove(yValue); + return ChartHighlight(xIndex: xIndex, dataSetIndex: dataSetIndex, stackIndex: stackIndex); + } + else + { + return nil; + } + } + + /// Returns the bounding box of the specified Entry in the specified DataSet. Returns null if the Entry could not be found in the charts data. + public func getBarBounds(e: BarChartDataEntry) -> CGRect! + { + var set = _data.getDataSetForEntry(e) as! BarChartDataSet!; + + if (set === nil) + { + return nil; + } + + var barspace = set.barSpace; + var y = CGFloat(e.value); + var x = CGFloat(e.xIndex); + + var barWidth: CGFloat = 0.5; + + var spaceHalf = barspace / 2.0; + var left = x - barWidth + spaceHalf; + var right = x + barWidth - spaceHalf; + var top = y >= 0.0 ? y : 0.0; + var bottom = y <= 0.0 ? y : 0.0; + + var bounds = CGRect(x: left, y: top, width: right - left, height: bottom - top); + + getTransformer(set.axisDependency).rectValueToPixel(&bounds); + + return bounds; + } + + // MARK: Accessors + + /// flag that enables or disables the highlighting arrow + public var drawHighlightArrowEnabled: Bool + { + get { return _drawHighlightArrowEnabled; } + set + { + _drawHighlightArrowEnabled = newValue; + setNeedsDisplay(); + } + } + + /// if set to true, all values are drawn above their bars, instead of below their top + public var drawValueAboveBarEnabled: Bool + { + get { return _drawValueAboveBarEnabled; } + set + { + _drawValueAboveBarEnabled = newValue; + setNeedsDisplay(); + } + } + + /// if set to true, all values of a stack are drawn individually, and not just their sum + public var drawValuesForWholeStackEnabled: Bool + { + get { return _drawValuesForWholeStackEnabled; } + set + { + _drawValuesForWholeStackEnabled = newValue; + setNeedsDisplay(); + } + } + + /// if set to true, a grey area is drawn behind each bar that indicates the maximum value + public var drawBarShadowEnabled: Bool + { + get { return _drawBarShadowEnabled; } + set + { + _drawBarShadowEnabled = newValue; + setNeedsDisplay(); + } + } + + /// returns true if drawing the highlighting arrow is enabled, false if not + public var isDrawHighlightArrowEnabled: Bool { return drawHighlightArrowEnabled; } + + /// returns true if drawing values above bars is enabled, false if not + public var isDrawValueAboveBarEnabled: Bool { return drawValueAboveBarEnabled; } + + /// returns true if all values of a stack are drawn, and not just their sum + public var isDrawValuesForWholeStackEnabled: Bool { return drawValuesForWholeStackEnabled; } + + /// returns true if drawing shadows (maxvalue) for each bar is enabled, false if not + public var isDrawBarShadowEnabled: Bool { return drawBarShadowEnabled; } + + // MARK: - BarChartRendererDelegate + + public func barChartRendererData(renderer: BarChartRenderer) -> BarChartData! + { + return _data as! BarChartData!; + } + + public func barChartRenderer(renderer: BarChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer! + { + return getTransformer(which); + } + + public func barChartRendererMaxVisibleValueCount(renderer: BarChartRenderer) -> Int + { + return maxVisibleValueCount; + } + + public func barChartDefaultRendererValueFormatter(renderer: BarChartRenderer) -> NSNumberFormatter! + { + return valueFormatter; + } + + public func barChartRendererChartXMax(renderer: BarChartRenderer) -> Float + { + return chartXMax; + } + + public func barChartIsDrawHighlightArrowEnabled(renderer: BarChartRenderer) -> Bool + { + return drawHighlightArrowEnabled; + } + + public func barChartIsDrawValueAboveBarEnabled(renderer: BarChartRenderer) -> Bool + { + return drawValueAboveBarEnabled; + } + + public func barChartIsDrawValuesForWholeStackEnabled(renderer: BarChartRenderer) -> Bool + { + return drawValuesForWholeStackEnabled; + } + + public func barChartIsDrawBarShadowEnabled(renderer: BarChartRenderer) -> Bool + { + return drawBarShadowEnabled; + } +} \ No newline at end of file diff --git a/Charts/Classes/Charts/BarLineChartViewBase.swift b/Charts/Classes/Charts/BarLineChartViewBase.swift new file mode 100644 index 0000000000..8acb911e99 --- /dev/null +++ b/Charts/Classes/Charts/BarLineChartViewBase.swift @@ -0,0 +1,1338 @@ +// +// BarLineChartViewBase.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// Base-class of LineChart, BarChart, ScatterChart and CandleStickChart. +public class BarLineChartViewBase: ChartViewBase +{ + /// the maximum number of entried to which values will be drawn + internal var _maxVisibleValueCount = 100 + + private var _pinchZoomEnabled = false + private var _doubleTapToZoomEnabled = true + private var _dragEnabled = true + + private var _scaleXEnabled = true + private var _scaleYEnabled = true + + /// the color for the background of the chart-drawing area (everything behind the grid lines). + public var gridBackgroundColor = UIColor(red: 240/255.0, green: 240/255.0, blue: 240/255.0, alpha: 1.0) + + public var borderColor = UIColor.blackColor() + public var borderLineWidth: CGFloat = 1.0 + + /// if set to true, the highlight indicator (lines for linechart, dark bar for barchart) will be drawn upon selecting values. + public var highlightIndicatorEnabled = true + + /// flag indicating if the grid background should be drawn or not + public var drawGridBackgroundEnabled = true + + /// Sets drawing the borders rectangle to true. If this is enabled, there is no point drawing the axis-lines of x- and y-axis. + public var drawBordersEnabled = false + + /// the object representing the labels on the y-axis, this object is prepared + /// in the pepareYLabels() method + internal var _leftAxis: ChartYAxis! + internal var _rightAxis: ChartYAxis! + + /// the object representing the labels on the x-axis + internal var _xAxis: ChartXAxis! + + internal var _leftYAxisRenderer: ChartYAxisRenderer! + internal var _rightYAxisRenderer: ChartYAxisRenderer! + + internal var _leftAxisTransformer: ChartTransformer! + internal var _rightAxisTransformer: ChartTransformer! + + internal var _xAxisRenderer: ChartXAxisRenderer! + + private var _tapGestureRecognizer: UITapGestureRecognizer! + private var _doubleTapGestureRecognizer: UITapGestureRecognizer! + private var _pinchGestureRecognizer: UIPinchGestureRecognizer! + private var _panGestureRecognizer: UIPanGestureRecognizer! + + /// flag that indicates if a custom viewport offset has been set + private var _customViewPortEnabled = false + + public override init(frame: CGRect) + { + super.init(frame: frame); + } + + public required init(coder aDecoder: NSCoder) + { + super.init(coder: aDecoder); + } + + internal override func initialize() + { + super.initialize(); + + _leftAxis = ChartYAxis(position: .Left); + _rightAxis = ChartYAxis(position: .Right); + + _xAxis = ChartXAxis(); + + _leftAxisTransformer = ChartTransformer(viewPortHandler: _viewPortHandler); + _rightAxisTransformer = ChartTransformer(viewPortHandler: _viewPortHandler); + + _leftYAxisRenderer = ChartYAxisRenderer(viewPortHandler: _viewPortHandler, yAxis: _leftAxis, transformer: _leftAxisTransformer); + _rightYAxisRenderer = ChartYAxisRenderer(viewPortHandler: _viewPortHandler, yAxis: _rightAxis, transformer: _rightAxisTransformer); + + _xAxisRenderer = ChartXAxisRenderer(viewPortHandler: _viewPortHandler, xAxis: _xAxis, transformer: _leftAxisTransformer); + + _tapGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("tapGestureRecognized:")); + _doubleTapGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("doubleTapGestureRecognized:")); + _doubleTapGestureRecognizer.numberOfTapsRequired = 2; + _pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: Selector("pinchGestureRecognized:")); + _panGestureRecognizer = UIPanGestureRecognizer(target: self, action: Selector("panGestureRecognized:")); + + self.addGestureRecognizer(_tapGestureRecognizer); + if (_doubleTapToZoomEnabled) + { + self.addGestureRecognizer(_doubleTapGestureRecognizer); + } + updateScaleGestureRecognizers(); + if (_dragEnabled) + { + self.addGestureRecognizer(_panGestureRecognizer); + } + } + + public override func drawRect(rect: CGRect) + { + super.drawRect(rect); + + if (_dataNotSet) + { + return; + } + + let context = UIGraphicsGetCurrentContext(); + + if (xAxis.isAdjustXLabelsEnabled) + { + calcModulus(); + } + + // execute all drawing commands + drawGridBackground(context: context); + + if (_leftAxis.isEnabled) + { + _leftYAxisRenderer?.computeAxis(yMin: _leftAxis.axisMinimum, yMax: _leftAxis.axisMaximum); + } + if (_rightAxis.isEnabled) + { + _rightYAxisRenderer?.computeAxis(yMin: _rightAxis.axisMinimum, yMax: _rightAxis.axisMaximum); + } + + _xAxisRenderer?.calcXBounds(_xAxisRenderer.transformer); + _leftYAxisRenderer?.calcXBounds(_xAxisRenderer.transformer); + _rightYAxisRenderer?.calcXBounds(_xAxisRenderer.transformer); + + _xAxisRenderer?.renderAxisLine(context: context); + _leftYAxisRenderer?.renderAxisLine(context: context); + _rightYAxisRenderer?.renderAxisLine(context: context); + + // make sure the graph values and grid cannot be drawn outside the content-rect + CGContextSaveGState(context); + + CGContextClipToRect(context, _viewPortHandler.contentRect); + + _xAxisRenderer?.renderGridLines(context: context); + _leftYAxisRenderer?.renderGridLines(context: context); + _rightYAxisRenderer?.renderGridLines(context: context); + + renderer?.drawData(context: context); + + _leftYAxisRenderer?.renderLimitLines(context: context); + _rightYAxisRenderer?.renderLimitLines(context: context); + + // if highlighting is enabled + if (highlightEnabled && highlightIndicatorEnabled && valuesToHighlight()) + { + renderer?.drawHighlighted(context: context, indices: _indicesToHightlight); + } + + // Removes clipping rectangle + CGContextRestoreGState(context); + + renderer!.drawExtras(context: context); + + _xAxisRenderer.renderAxisLabels(context: context); + _leftYAxisRenderer.renderAxisLabels(context: context); + _rightYAxisRenderer.renderAxisLabels(context: context); + + renderer!.drawValues(context: context); + + _legendRenderer.renderLegend(context: context, legend: _legend); + // drawLegend(); + + drawMarkers(context: context); + + drawDescription(context: context); + } + + internal func prepareValuePxMatrix() + { + _rightAxisTransformer.prepareMatrixValuePx(chartXMin: _chartXMin, deltaX: _deltaX, deltaY: CGFloat(_rightAxis.axisRange), chartYMin: _rightAxis.axisMinimum); + _leftAxisTransformer.prepareMatrixValuePx(chartXMin: _chartXMin, deltaX: _deltaX, deltaY: CGFloat(_leftAxis.axisRange), chartYMin: _leftAxis.axisMinimum); + } + + internal func prepareOffsetMatrix() + { + _rightAxisTransformer.prepareMatrixOffset(_rightAxis.isInverted); + _leftAxisTransformer.prepareMatrixOffset(_leftAxis.isInverted); + } + + public override func notifyDataSetChanged() + { + if (_dataNotSet) + { + return; + } + + calcMinMax(); + + _leftAxis?._defaultValueFormatter = _defaultValueFormatter; + _rightAxis?._defaultValueFormatter = _defaultValueFormatter; + + _leftYAxisRenderer?.computeAxis(yMin: _leftAxis.axisMinimum, yMax: _leftAxis.axisMaximum); + _rightYAxisRenderer?.computeAxis(yMin: _rightAxis.axisMinimum, yMax: _rightAxis.axisMaximum); + + _xAxisRenderer?.computeAxis(xValAverageLength: _data.xValAverageLength, xValues: _data.xVals); + + _legend = _legendRenderer?.computeLegend(_data, legend: _legend); + + calculateOffsets(); + + setNeedsDisplay(); + } + + internal override func calcMinMax() + { + var minLeft = _data.getYMin(.Left); + var maxLeft = _data.getYMax(.Left); + var minRight = _data.getYMin(.Right); + var maxRight = _data.getYMax(.Right); + + var leftRange = abs(maxLeft - (_leftAxis.isStartAtZeroEnabled ? 0.0 : minLeft)); + var rightRange = abs(maxRight - (_rightAxis.isStartAtZeroEnabled ? 0.0 : minRight)); + + var topSpaceLeft = leftRange * Float(_leftAxis.spaceTop); + var topSpaceRight = rightRange * Float(_rightAxis.spaceTop); + var bottomSpaceLeft = leftRange * Float(_leftAxis.spaceBottom); + var bottomSpaceRight = rightRange * Float(_rightAxis.spaceBottom); + + _chartXMax = Float(_data.xVals.count - 1); + _deltaX = CGFloat(abs(_chartXMax - _chartXMin)); + + _leftAxis.axisMaximum = !isnan(_leftAxis.customAxisMax) ? _leftAxis.customAxisMax : (maxLeft + topSpaceLeft); + _rightAxis.axisMaximum = !isnan(_rightAxis.customAxisMax) ? _rightAxis.customAxisMax : (maxRight + topSpaceRight); + _leftAxis.axisMinimum = !isnan(_leftAxis.customAxisMin) ? _leftAxis.customAxisMin : (minLeft - bottomSpaceLeft); + _rightAxis.axisMinimum = !isnan(_rightAxis.customAxisMin) ? _rightAxis.customAxisMin : (minRight - bottomSpaceRight); + + // consider starting at zero (0) + if (_leftAxis.isStartAtZeroEnabled) + { + _leftAxis.axisMinimum = 0.0; + } + + if (_rightAxis.isStartAtZeroEnabled) + { + _rightAxis.axisMinimum = 0.0; + } + + _leftAxis.axisRange = abs(_leftAxis.axisMaximum - _leftAxis.axisMinimum); + _rightAxis.axisRange = abs(_rightAxis.axisMaximum - _rightAxis.axisMinimum); + } + + internal override func calculateOffsets() + { + if (!_customViewPortEnabled) + { + var offsetLeft = CGFloat(0.0); + var offsetRight = CGFloat(0.0); + var offsetTop = CGFloat(0.0); + var offsetBottom = CGFloat(0.0); + + // setup offsets for legend + if (_legend !== nil && _legend.isEnabled) + { + if (_legend.position == .RightOfChart + || _legend.position == .RightOfChartCenter) + { + offsetRight += _legend.textWidthMax + _legend.xOffset * 2.0; + } + else if (_legend.position == .BelowChartLeft + || _legend.position == .BelowChartRight + || _legend.position == .BelowChartCenter) + { + + offsetBottom += _legend.textHeightMax * 3.0; + } + } + + // offsets for y-labels + if (leftAxis.needsOffset) + { + offsetLeft += leftAxis.requiredSize().width; + } + + if (rightAxis.needsOffset) + { + offsetRight += rightAxis.requiredSize().width; + } + + var xlabelheight = xAxis.labelHeight * 2.0; + + if (xAxis.isEnabled) + { + // offsets for x-labels + if (xAxis.labelPosition == .Bottom) + { + offsetBottom += xlabelheight; + } + else if (xAxis.labelPosition == .Top) + { + offsetTop += xlabelheight; + } + else if (xAxis.labelPosition == .BothSided) + { + offsetBottom += xlabelheight; + offsetTop += xlabelheight; + } + } + + var min = CGFloat(10.0); + + _viewPortHandler.restrainViewPort( + offsetLeft: max(min, offsetLeft), + offsetTop: max(min, offsetTop), + offsetRight: max(min, offsetRight), + offsetBottom: max(min, offsetBottom)); + } + + prepareOffsetMatrix(); + prepareValuePxMatrix(); + } + + + /// calculates the modulus for x-labels and grid + internal func calcModulus() + { + if (_xAxis === nil) + { + return; + } + + _xAxis.axisLabelModulus = Int(ceil((CGFloat(_data.xValCount) * _xAxis.labelWidth) / (_viewPortHandler.contentWidth * _viewPortHandler.touchMatrix.a))); + + if (_xAxis.axisLabelModulus < 1) + { + _xAxis.axisLabelModulus = 1; + } + } + + public override func getMarkerPosition(#entry: ChartDataEntry, dataSetIndex: Int) -> CGPoint + { + var xPos = CGFloat(entry.xIndex); + + if (self.isKindOfClass(BarChartView)) + { + var bd = _data as! BarChartData; + var space = bd.groupSpace; + var j = _data.getDataSetByIndex(dataSetIndex)!.entryIndex(entry: entry, isEqual: true); + + var x = CGFloat(j * (_data.dataSetCount - 1) + dataSetIndex) + space * CGFloat(j) + space / 2.0; + + xPos += x; + } + + // position of the marker depends on selected value index and value + var pt = CGPoint(x: xPos, y: CGFloat(entry.value) * _animator.phaseY); + + getTransformer(_data.getDataSetByIndex(dataSetIndex)!.axisDependency).pointValueToPixel(&pt); + + return pt; + } + + /// draws the grid background + internal func drawGridBackground(#context: CGContext) + { + if (drawGridBackgroundEnabled || drawBordersEnabled) + { + CGContextSaveGState(context); + } + + if (drawGridBackgroundEnabled) + { + // draw the grid background + CGContextSetFillColorWithColor(context, gridBackgroundColor.CGColor); + CGContextFillRect(context, _viewPortHandler.contentRect); + } + + if (drawBordersEnabled) + { + CGContextSetLineWidth(context, borderLineWidth); + CGContextSetStrokeColorWithColor(context, borderColor.CGColor); + CGContextStrokeRect(context, _viewPortHandler.contentRect); + } + + if (drawGridBackgroundEnabled || drawBordersEnabled) + { + CGContextRestoreGState(context); + } + } + + /// Returns the Transformer class that contains all matrices and is + /// responsible for transforming values into pixels on the screen and + /// backwards. + public func getTransformer(which: ChartYAxis.AxisDependency) -> ChartTransformer + { + if (which == .Left) + { + return _leftAxisTransformer; + } + else + { + return _rightAxisTransformer; + } + } + + // MARK: - Scaling and Gestures + + private var _gestureStartMatrix = CGAffineTransformIdentity; + private var _gestureScaleMatrix = CGAffineTransformIdentity; + private var _gesturePanMatrix = CGAffineTransformIdentity; + + private enum GestureScaleAxis + { + case Both + case X + case Y + } + + private var _isDragging = false; + private var _isScaling = false; + private var _gestureScaleAxis = GestureScaleAxis.Both; + private var _closestDataSetToTouch: ChartDataSet!; + + /// the last highlighted object + private var _lastHighlighted: ChartHighlight!; + + @objc private func tapGestureRecognized(recognizer: UITapGestureRecognizer) + { + if (_dataNotSet) + { + return; + } + + if (recognizer.state == UIGestureRecognizerState.Ended) + { + var h = getHighlightByTouchPoint(recognizer.locationInView(self)); + + if (h === nil || h!.isEqual(_lastHighlighted)) + { + self.highlightValue(highlight: nil, callDelegate: true); + _lastHighlighted = nil; + } + else + { + _lastHighlighted = h; + self.highlightValue(highlight: h, callDelegate: true); + } + } + } + + @objc private func doubleTapGestureRecognized(recognizer: UITapGestureRecognizer) + { + if (_dataNotSet) + { + return; + } + + if (recognizer.state == UIGestureRecognizerState.Ended) + { + if (!_dataNotSet && _doubleTapToZoomEnabled) + { + var location = recognizer.locationInView(self); + location.x = location.x - _viewPortHandler.offsetLeft; + + if (isAnyAxisInverted && _closestDataSetToTouch !== nil && getAxis(_closestDataSetToTouch.axisDependency).isInverted) + { + location.y = -(location.y - _viewPortHandler.offsetTop); + } + else + { + location.y = -(self.bounds.size.height - location.y - _viewPortHandler.offsetBottom); + } + + self.zoom(1.4, scaleY: 1.4, x: location.x, y: location.y); + } + } + } + + @objc private func pinchGestureRecognized(recognizer: UIPinchGestureRecognizer) + { + if (recognizer.state == UIGestureRecognizerState.Began) + { + if (!_dataNotSet && (_pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled)) + { + if (!_isDragging) + { + _gestureStartMatrix = _viewPortHandler.touchMatrix; + } + _isScaling = true; + + if (_pinchZoomEnabled) + { + _gestureScaleAxis = .Both; + } + else + { + var x = abs(recognizer.locationInView(self).x - recognizer.locationOfTouch(1, inView: self).x); + var y = abs(recognizer.locationInView(self).y - recognizer.locationOfTouch(1, inView: self).y); + + if (x > y) + { + _gestureScaleAxis = .X; + } + else + { + _gestureScaleAxis = .Y; + } + } + } + } + else if (recognizer.state == UIGestureRecognizerState.Ended) + { + if (_isScaling) + { + _isScaling = false; + + var location = recognizer.locationInView(self); + location.x = location.x - _viewPortHandler.offsetLeft; + + if (isAnyAxisInverted && _closestDataSetToTouch !== nil && getAxis(_closestDataSetToTouch.axisDependency).isInverted) + { + location.y = -(location.y - _viewPortHandler.offsetTop); + } + else + { + location.y = -(self.bounds.size.height - location.y - _viewPortHandler.offsetBottom); + } + + _gestureScaleMatrix = CGAffineTransformMakeTranslation(location.x, location.y); + _gestureScaleMatrix = CGAffineTransformScale(_gestureScaleMatrix, + (_gestureScaleAxis == .Both || _gestureScaleAxis == .X) && _scaleXEnabled ? recognizer.scale : 1.0, + (_gestureScaleAxis == .Both || _gestureScaleAxis == .Y) && _scaleYEnabled ? recognizer.scale : 1.0); + _gestureScaleMatrix = CGAffineTransformTranslate(_gestureScaleMatrix, + -location.x, -location.y); + + var matrix = CGAffineTransformConcat(_gestureStartMatrix, _gestureScaleMatrix); + if (_isDragging) + { + matrix = CGAffineTransformConcat(matrix, _gesturePanMatrix); + } + + _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true); + + // Save the matrix changes to the _gestureStartMatrix + + _gestureStartMatrix = CGAffineTransformConcat(_gestureStartMatrix, _gestureScaleMatrix); + } + } + else if (recognizer.state == UIGestureRecognizerState.Cancelled) + { + if (_isScaling) + { + _isScaling = false; + + _viewPortHandler.refresh(newMatrix: _gestureStartMatrix, chart: self, invalidate: true); + } + } + else if (recognizer.state == UIGestureRecognizerState.Changed) + { + if (_isScaling) + { + var location = recognizer.locationInView(self); + location.x = location.x - _viewPortHandler.offsetLeft; + + if (isAnyAxisInverted && _closestDataSetToTouch !== nil && getAxis(_closestDataSetToTouch.axisDependency).isInverted) + { + location.y = -(location.y - _viewPortHandler.offsetTop); + } + else + { + location.y = -(_viewPortHandler.chartHeight - location.y - _viewPortHandler.offsetBottom); + } + + _gestureScaleMatrix = CGAffineTransformMakeTranslation(location.x, location.y); + _gestureScaleMatrix = CGAffineTransformScale(_gestureScaleMatrix, + (_gestureScaleAxis == .Both || _gestureScaleAxis == .X) && _scaleXEnabled ? recognizer.scale : 1.0, + (_gestureScaleAxis == .Both || _gestureScaleAxis == .Y) && _scaleYEnabled ? recognizer.scale : 1.0); + _gestureScaleMatrix = CGAffineTransformTranslate(_gestureScaleMatrix, + -location.x, -location.y); + + var matrix = CGAffineTransformConcat(_gestureStartMatrix, _gestureScaleMatrix); + if (_isDragging) + { + matrix = CGAffineTransformConcat(matrix, _gesturePanMatrix); + } + + _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true); + } + } + } + + @objc private func panGestureRecognized(recognizer: UIPanGestureRecognizer) + { + if (recognizer.state == UIGestureRecognizerState.Began) + { + if (!_dataNotSet && _dragEnabled && !self.hasNoDragOffset || !self.isFullyZoomedOut) + { + if (!_isScaling) + { + _gestureStartMatrix = _viewPortHandler.touchMatrix; + } + _isDragging = true; + + _closestDataSetToTouch = getDataSetByTouchPoint(recognizer.locationOfTouch(0, inView: self)); + } + } + else if (recognizer.state == UIGestureRecognizerState.Ended) + { + if (_isDragging) + { + _isDragging = false; + + var translation = recognizer.translationInView(self); + + if (isAnyAxisInverted && _closestDataSetToTouch !== nil + && getAxis(_closestDataSetToTouch.axisDependency).isInverted) + { + translation.y = -translation.y; + } + + _gesturePanMatrix = CGAffineTransformMakeTranslation(translation.x, translation.y); + + var matrix = _isScaling ? CGAffineTransformConcat(_gestureStartMatrix, _gestureScaleMatrix) : _gestureStartMatrix; + matrix = CGAffineTransformConcat(matrix, _gesturePanMatrix); + + _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true); + + // Save the matrix changes to the _gestureStartMatrix + + if (_isScaling) + { + translation = CGPointApplyAffineTransform(translation, _gestureScaleMatrix); + _gesturePanMatrix = CGAffineTransformMakeTranslation(translation.x, translation.y); + } + + _gestureStartMatrix = CGAffineTransformConcat(_gestureStartMatrix, _gesturePanMatrix); + } + } + else if (recognizer.state == UIGestureRecognizerState.Cancelled) + { + if (_isDragging) + { + _isDragging = false; + + _viewPortHandler.refresh(newMatrix: _gestureStartMatrix, chart: self, invalidate: true); + } + } + else if (recognizer.state == UIGestureRecognizerState.Changed) + { + if (_isDragging) + { + var translation = recognizer.translationInView(self); + + if (isAnyAxisInverted && _closestDataSetToTouch !== nil + && getAxis(_closestDataSetToTouch.axisDependency).isInverted) + { + translation.y = -translation.y; + } + + _gesturePanMatrix = CGAffineTransformMakeTranslation(translation.x, translation.y); + + var matrix = _isScaling ? CGAffineTransformConcat(_gestureStartMatrix, _gestureScaleMatrix) : _gestureStartMatrix; + matrix = CGAffineTransformConcat(matrix, _gesturePanMatrix); + + _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true); + } + } + } + + /// Zooms in by 1.4f, into the charts center. center. + public func zoomIn() + { + var matrix = _viewPortHandler.zoomIn(x: self.bounds.size.width / 2.0, y: -(self.bounds.size.height / 2.0)); + _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true); + } + + /// Zooms out by 0.7f, from the charts center. center. + public func zoomOut() + { + var matrix = _viewPortHandler.zoomOut(x: self.bounds.size.width / 2.0, y: -(self.bounds.size.height / 2.0)); + _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true); + } + + /// Zooms in or out by the given scale factor. x and y are the coordinates + /// (in pixels) of the zoom center. + /// + /// :param: scaleX if < 1f --> zoom out, if > 1f --> zoom in + /// :param: scaleY if < 1f --> zoom out, if > 1f --> zoom in + /// :param: x + /// :param: y + public func zoom(scaleX: CGFloat, scaleY: CGFloat, x: CGFloat, y: CGFloat) + { + var matrix = _viewPortHandler.zoom(scaleX: scaleX, scaleY: scaleY, x: x, y: -y); + _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true); + } + + /// Resets all zooming and dragging and makes the chart fit exactly it's bounds. + public func fitScreen() + { + var matrix = _viewPortHandler.fitScreen(); + _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: true); + } + + /// Sets the minimum scale value to which can be zoomed out. 1f = fitScreen + public func setScaleMinima(scaleX: CGFloat, scaleY: CGFloat) + { + _viewPortHandler.setMinimumScaleX(scaleX); + _viewPortHandler.setMinimumScaleY(scaleY); + } + + /// Sets the size of the area (range on the x-axis) that should be maximum + /// visible at once. If this is e.g. set to 10, no more than 10 values on the + /// x-axis can be viewed at once without scrolling. + public func setVisibleXRange(xRange: CGFloat) + { + var xScale = _deltaX / (xRange + 0.01); + _viewPortHandler.setMinimumScaleX(xScale); + } + + /// Sets the size of the area (range on the y-axis) that should be maximum visible at once. + /// + /// :param: yRange + /// :param: axis - the axis for which this limit should apply + public func setVisibleYRange(yRange: CGFloat, axis: ChartYAxis.AxisDependency) + { + var yScale = getDeltaY(axis) / yRange; + _viewPortHandler.setMinimumScaleY(yScale); + } + + /// Moves the left side of the current viewport to the specified x-index. + public func moveViewToX(xIndex: Int) + { + var pt = CGPoint(x: CGFloat(xIndex), y: 0.0); + + getTransformer(.Left).pointValueToPixel(&pt); + _viewPortHandler.centerViewPort(pt: pt, chart: self); + } + + /// Centers the viewport to the specified y-value on the y-axis. + /// + /// :param: yValue + /// :param: axis - which axis should be used as a reference for the y-axis + public func moveViewToY(yValue: CGFloat, axis: ChartYAxis.AxisDependency) + { + var valsInView = getDeltaY(axis) / _viewPortHandler.scaleY; + + var pt = CGPoint(x: 0.0, y: yValue + valsInView / 2.0); + + getTransformer(axis).pointValueToPixel(&pt); + _viewPortHandler.centerViewPort(pt: pt, chart: self); + } + + /// This will move the left side of the current viewport to the specified + /// x-index on the x-axis, and center the viewport to the specified y-value + /// on the y-axis. + /// + /// :param: xIndex + /// :param: yValue + /// :param: axis - which axis should be used as a reference for the y-axis + public func moveViewTo(xIndex: Int, yValue: CGFloat, axis: ChartYAxis.AxisDependency) + { + var valsInView = getDeltaY(axis) / _viewPortHandler.scaleY; + + var pt = CGPoint(x: CGFloat(xIndex), y: yValue + valsInView / 2.0); + + getTransformer(axis).pointValueToPixel(&pt); + _viewPortHandler.centerViewPort(pt: pt, chart: self); + } + + /// Sets custom offsets for the current ViewPort (the offsets on the sides of the actual chart window). Setting this will prevent the chart from automatically calculating it's offsets. Use resetViewPortOffsets() to undo this. + public func setViewPortOffsets(#left: CGFloat, top: CGFloat, right: CGFloat, bottom: CGFloat) + { + _customViewPortEnabled = true; + + if (NSThread.isMainThread()) + { + self._viewPortHandler.restrainViewPort(offsetLeft: left, offsetTop: top, offsetRight: right, offsetBottom: bottom); + } + else + { + dispatch_async(dispatch_get_main_queue(), { + self._viewPortHandler.restrainViewPort(offsetLeft: left, offsetTop: top, offsetRight: right, offsetBottom: bottom); + }); + } + } + + /// Resets all custom offsets set via setViewPortOffsets(...) method. Allows the chart to again calculate all offsets automatically. + public func resetViewPortOffsets() + { + _customViewPortEnabled = false; + calculateOffsets(); + } + + // MARK: - Accessors + + /// Returns the delta-y value (y-value range) of the specified axis. + public func getDeltaY(axis: ChartYAxis.AxisDependency) -> CGFloat + { + if (axis == .Left) + { + return CGFloat(leftAxis.axisRange); + } + else + { + return CGFloat(rightAxis.axisRange); + } + } + + /// Returns the position (in pixels) the provided Entry has inside the chart view + public func getPosition(e: ChartDataEntry, axis: ChartYAxis.AxisDependency) -> CGPoint + { + var vals = CGPoint(x: CGFloat(e.xIndex), y: CGFloat(e.value)); + + getTransformer(axis).pointValueToPixel(&vals); + + return vals; + } + + /// the number of maximum visible drawn values on the chart + /// only active when setDrawValues() is enabled + public var maxVisibleValueCount: Int + { + get + { + return _maxVisibleValueCount; + } + set + { + _maxVisibleValueCount = newValue; + } + } + + /// If set to true, the highlight indicators (cross of two lines for + /// LineChart and ScatterChart, dark bar overlay for BarChart) that give + /// visual indication that an Entry has been selected will be drawn upon + /// selecting values. This does not depend on the MarkerView. + /// :default: true + public var isHighlightIndicatorEnabled: Bool + { + return highlightIndicatorEnabled; + } + + /// is dragging enabled? (moving the chart with the finger) for the chart (this does not affect scaling). + public var dragEnabled: Bool + { + get + { + return _dragEnabled; + } + set + { + if (_dragEnabled != newValue) + { + _dragEnabled = newValue; + if (_dragEnabled) + { + self.addGestureRecognizer(_panGestureRecognizer); + } + else + { + if (self.gestureRecognizers != nil) + { + for (var i = 0; i < self.gestureRecognizers!.count; i++) + { + if (self.gestureRecognizers?[i] === _panGestureRecognizer) + { + self.gestureRecognizers!.removeAtIndex(i); + break; + } + } + } + } + } + } + } + + /// is dragging enabled? (moving the chart with the finger) for the chart (this does not affect scaling). + public var isDragEnabled: Bool + { + return dragEnabled; + } + + /// is scaling enabled? (zooming in and out by gesture) for the chart (this does not affect dragging). + public func setScaleEnabled(enabled: Bool) + { + if (_scaleXEnabled != enabled || _scaleYEnabled != enabled) + { + _scaleXEnabled = enabled; + _scaleYEnabled = enabled; + updateScaleGestureRecognizers(); + } + } + + public var scaleXEnabled: Bool + { + get + { + return _scaleXEnabled + } + set + { + if (_scaleXEnabled != newValue) + { + _scaleXEnabled = newValue; + updateScaleGestureRecognizers(); + } + } + } + + public var scaleYEnabled: Bool + { + get + { + return _scaleYEnabled + } + set + { + if (_scaleYEnabled != newValue) + { + _scaleYEnabled = newValue; + updateScaleGestureRecognizers(); + } + } + } + + public var isScaleXEnabled: Bool { return scaleXEnabled; } + public var isScaleYEnabled: Bool { return scaleYEnabled; } + + /// flag that indicates if double tap zoom is enabled or not + public var doubleTapToZoomEnabled: Bool + { + get + { + return _doubleTapToZoomEnabled; + } + set + { + if (_doubleTapToZoomEnabled != newValue) + { + _doubleTapToZoomEnabled = newValue; + if (_doubleTapToZoomEnabled) + { + self.addGestureRecognizer(_doubleTapGestureRecognizer); + } + else + { + if (self.gestureRecognizers != nil) + { + for (var i = 0; i < self.gestureRecognizers!.count; i++) + { + if (self.gestureRecognizers?[i] === _doubleTapGestureRecognizer) + { + self.gestureRecognizers!.removeAtIndex(i); + break; + } + } + } + } + } + } + } + + /// :returns: true if zooming via double-tap is enabled false if not. + /// :default: true + public var isDoubleTapToZoomEnabled: Bool + { + return doubleTapToZoomEnabled; + } + + /// :returns: true if drawing the grid background is enabled, false if not. + /// :default: true + public var isDrawGridBackgroundEnabled: Bool + { + return drawGridBackgroundEnabled; + } + + /// :returns: true if drawing the borders rectangle is enabled, false if not. + /// :default: false + public var isDrawBordersEnabled: Bool + { + return drawBordersEnabled; + } + + /// Returns the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point inside the Line-, Scatter-, or CandleStick-Chart. + public func getHighlightByTouchPoint(var pt: CGPoint) -> ChartHighlight! + { + if (_dataNotSet || _data === nil) + { + println("Can't select by touch. No data set."); + return nil; + } + + var valPt = CGPoint(); + valPt.x = pt.x; + valPt.y = 0.0; + + // take any transformer to determine the x-axis value + _leftAxisTransformer.pixelToValue(&valPt); + + var xTouchVal = valPt.x; + var base = floor(xTouchVal); + + var touchOffset = _deltaX * 0.025; + + // touch out of chart + if (xTouchVal < -touchOffset || xTouchVal > _deltaX + touchOffset) + { + return nil; + } + + if (base < 0.0) + { + base = 0.0; + } + + if (base >= _deltaX) + { + base = _deltaX - 1.0; + } + + var xIndex = Int(base); + + // check if we are more than half of a x-value or not + if (xTouchVal - base > 0.5) + { + xIndex = Int(base + 1.0); + } + + var valsAtIndex = getYValsAtIndex(xIndex); + + var leftdist = ChartUtils.getMinimumDistance(valsAtIndex, val: Float(pt.y), axis: .Left); + var rightdist = ChartUtils.getMinimumDistance(valsAtIndex, val: Float(pt.y), axis: .Right); + + if (_data!.getFirstRight() === nil) + { + rightdist = FLT_MAX; + } + if (_data!.getFirstLeft() === nil) + { + leftdist = FLT_MAX; + } + + var axis: ChartYAxis.AxisDependency = leftdist < rightdist ? .Left : .Right; + + var dataSetIndex = ChartUtils.closestDataSetIndex(valsAtIndex, value: Float(pt.y), axis: axis); + + if (dataSetIndex == -1) + { + return nil; + } + + return ChartHighlight(xIndex: xIndex, dataSetIndex: dataSetIndex); + } + + /// Returns an array of SelInfo objects for the given x-index. The SelInfo + /// objects give information about the value at the selected index and the + /// DataSet it belongs to. + public func getYValsAtIndex(xIndex: Int) -> [ChartSelInfo] + { + var vals = [ChartSelInfo](); + + var pt = CGPoint(); + + for (var i = 0, count = _data.dataSetCount; i < count; i++) + { + var dataSet = _data.getDataSetByIndex(i); + if (dataSet === nil) + { + continue; + } + + // extract all y-values from all DataSets at the given x-index + var yVal = dataSet!.yValForXIndex(xIndex); + pt.y = CGFloat(yVal); + + getTransformer(dataSet!.axisDependency).pointValueToPixel(&pt); + + if (!isnan(pt.y)) + { + vals.append(ChartSelInfo(value: Float(pt.y), dataSetIndex: i, dataSet: dataSet!)); + } + } + + return vals; + } + + /// Returns the x and y values in the chart at the given touch point + /// (encapsulated in a PointD). This method transforms pixel coordinates to + /// coordinates / values in the chart. This is the opposite method to + /// getPixelsForValues(...). + public func getValueByTouchPoint(var #pt: CGPoint, axis: ChartYAxis.AxisDependency) -> CGPoint + { + getTransformer(axis).pixelToValue(&pt); + + return pt; + } + + /// Transforms the given chart values into pixels. This is the opposite + /// method to getValueByTouchPoint(...). + public func getPixelForValue(x: Float, y: Float, axis: ChartYAxis.AxisDependency) -> CGPoint + { + var pt = CGPoint(x: CGFloat(x), y: CGFloat(y)); + + getTransformer(axis).pointValueToPixel(&pt); + + return pt; + } + + /// returns the y-value at the given touch position (must not necessarily be + /// a value contained in one of the datasets) + public func getYValueByTouchPoint(#pt: CGPoint, axis: ChartYAxis.AxisDependency) -> CGFloat + { + return getValueByTouchPoint(pt: pt, axis: axis).y; + } + + /// returns the Entry object displayed at the touched position of the chart + public func getEntryByTouchPoint(pt: CGPoint) -> ChartDataEntry! + { + var h = getHighlightByTouchPoint(pt); + if (h !== nil) + { + return _data!.getEntryForHighlight(h!); + } + return nil; + } + + ///returns the DataSet object displayed at the touched position of the chart + public func getDataSetByTouchPoint(pt: CGPoint) -> BarLineScatterCandleChartDataSet! + { + var h = getHighlightByTouchPoint(pt); + if (h !== nil) + { + return _data.getDataSetByIndex(h.dataSetIndex) as! BarLineScatterCandleChartDataSet!; + } + return nil; + } + + /// Returns the lowest x-index (value on the x-axis) that is still visible on he chart. + public var lowestVisibleXIndex: Int + { + var pt = CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom); + getTransformer(.Left).pixelToValue(&pt); + return (pt.x <= 0.0) ? 0 : Int(pt.x + 1.0); + } + + /// Returns the highest x-index (value on the x-axis) that is still visible on the chart. + public var highestVisibleXIndex: Int + { + var pt = CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentBottom); + getTransformer(.Left).pixelToValue(&pt); + return (Int(pt.x) >= _data.xValCount) ? _data.xValCount - 1 : Int(pt.x); + } + + /// returns the current x-scale factor + public var scaleX: CGFloat { return _viewPortHandler.scaleX; } + + /// returns the current y-scale factor + public var scaleY: CGFloat { return _viewPortHandler.scaleY; } + + /// if the chart is fully zoomed out, return true + public var isFullyZoomedOut: Bool { return _viewPortHandler.isFullyZoomedOut; } + + /// Returns the left y-axis object. In the horizontal bar-chart, this is the + /// top axis. + public var leftAxis: ChartYAxis + { + return _leftAxis; + } + + /// Returns the right y-axis object. In the horizontal bar-chart, this is the + /// bottom axis. + public var rightAxis: ChartYAxis { return _rightAxis; } + + /// Returns the y-axis object to the corresponding AxisDependency. In the + /// horizontal bar-chart, LEFT == top, RIGHT == BOTTOM + public func getAxis(axis: ChartYAxis.AxisDependency) -> ChartYAxis + { + if (axis == .Left) + { + return _leftAxis; + } + else + { + return _rightAxis; + } + } + + /// Returns the object representing all x-labels, this method can be used to + /// acquire the XAxis object and modify it (e.g. change the position of the + /// labels) + public var xAxis: ChartXAxis + { + return _xAxis; + } + + /// flag that indicates if pinch-zoom is enabled. if true, both x and y axis can be scaled with 2 fingers, if false, x and y axis can be scaled separately + public var pinchZoomEnabled: Bool + { + get + { + return _pinchZoomEnabled; + } + set + { + if (_pinchZoomEnabled != newValue) + { + _pinchZoomEnabled = newValue; + updateScaleGestureRecognizers(); + } + } + } + + private func updateScaleGestureRecognizers() + { + if (self.gestureRecognizers != nil) + { + for (var i = 0; i < self.gestureRecognizers!.count; i++) + { + if (self.gestureRecognizers![i] === _pinchGestureRecognizer) + { + self.gestureRecognizers!.removeAtIndex(i); + break; + } + } + } + + if (_pinchZoomEnabled || _scaleXEnabled || _scaleYEnabled) + { + self.addGestureRecognizer(_pinchGestureRecognizer); + } + } + + /// returns true if pinch-zoom is enabled, false if not + /// :default: false + public var isPinchZoomEnabled: Bool { return pinchZoomEnabled; } + + /// Set an offset in dp that allows the user to drag the chart over it's + /// bounds on the x-axis. + public func setDragOffsetX(offset: CGFloat) + { + _viewPortHandler.setDragOffsetX(offset); + } + + /// Set an offset in dp that allows the user to drag the chart over it's + /// bounds on the y-axis. + public func setDragOffsetY(offset: CGFloat) + { + _viewPortHandler.setDragOffsetY(offset); + } + + /// :returns: true if both drag offsets (x and y) are zero or smaller. + public var hasNoDragOffset: Bool { return _viewPortHandler.hasNoDragOffset; } + + public var xAxisRenderer: ChartXAxisRenderer { return _xAxisRenderer; } + + public var leftYAxisRenderer: ChartYAxisRenderer { return _leftYAxisRenderer; } + + public var rightYAxisRenderer: ChartYAxisRenderer { return _rightYAxisRenderer; } + + public override var chartYMax: Float + { + return max(leftAxis.axisMaximum, rightAxis.axisMaximum); + } + + public override var chartYMin: Float + { + return min(leftAxis.axisMinimum, rightAxis.axisMinimum); + } + + /// Returns true if either the left or the right or both axes are inverted. + public var isAnyAxisInverted: Bool + { + return _leftAxis.isInverted || _rightAxis.isInverted; + } +} + +/// Default formatter that calculates the position of the filled line. +internal class BarLineChartFillFormatter: NSObject, ChartFillFormatter +{ + private weak var _chart: BarLineChartViewBase!; + + internal init(chart: BarLineChartViewBase) + { + _chart = chart; + } + + internal func getFillLinePosition(#dataSet: LineChartDataSet, data: LineChartData, chartMaxY: Float, chartMinY: Float) -> CGFloat + { + var fillMin = CGFloat(0.0); + + if (dataSet.yMax > 0.0 && dataSet.yMin < 0.0) + { + fillMin = 0.0; + } + else + { + if (!_chart.getAxis(dataSet.axisDependency).isStartAtZeroEnabled) + { + var max: Float, min: Float; + + if (data.yMax > 0.0) + { + max = 0.0; + } + else + { + max = chartMaxY; + } + + if (data.yMin < 0.0) + { + min = 0.0; + } + else + { + min = chartMinY; + } + + fillMin = CGFloat(dataSet.yMin >= 0.0 ? min : max); + } + else + { + fillMin = 0.0; + } + } + + return fillMin; + } +} \ No newline at end of file diff --git a/Charts/Classes/Charts/CandleStickChartView.swift b/Charts/Classes/Charts/CandleStickChartView.swift new file mode 100644 index 0000000000..03642d8ef5 --- /dev/null +++ b/Charts/Classes/Charts/CandleStickChartView.swift @@ -0,0 +1,76 @@ +// +// CandleStickChartView.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// Financial chart type that draws candle-sticks. +public class CandleStickChartView: BarLineChartViewBase, CandleStickChartRendererDelegate +{ + internal override func initialize() + { + super.initialize(); + + renderer = CandleStickChartRenderer(delegate: self, animator: _animator, viewPortHandler: _viewPortHandler); + _chartXMin = -0.5; + } + + internal override func calcMinMax() + { + super.calcMinMax(); + + _chartXMax += 0.5; + _deltaX = CGFloat(abs(_chartXMax - _chartXMin)); + } + + // MARK: - CandleStickChartRendererDelegate + + public func candleStickChartRendererCandleData(renderer: CandleStickChartRenderer) -> CandleChartData! + { + return _data as! CandleChartData!; + } + + public func candleStickChartRenderer(renderer: CandleStickChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer! + { + return self.getTransformer(which); + } + + public func candleStickChartDefaultRendererValueFormatter(renderer: CandleStickChartRenderer) -> NSNumberFormatter! + { + return self.valueFormatter; + } + + public func candleStickChartRendererChartYMax(renderer: CandleStickChartRenderer) -> Float + { + return self.chartYMax; + } + + public func candleStickChartRendererChartYMin(renderer: CandleStickChartRenderer) -> Float + { + return self.chartYMin; + } + + public func candleStickChartRendererChartXMax(renderer: CandleStickChartRenderer) -> Float + { + return self.chartXMax; + } + + public func candleStickChartRendererChartXMin(renderer: CandleStickChartRenderer) -> Float + { + return self.chartXMin; + } + + public func candleStickChartRendererMaxVisibleValueCount(renderer: CandleStickChartRenderer) -> Int + { + return self.maxVisibleValueCount; + } +} \ No newline at end of file diff --git a/Charts/Classes/Charts/ChartViewBase.swift b/Charts/Classes/Charts/ChartViewBase.swift new file mode 100644 index 0000000000..1affadb9c3 --- /dev/null +++ b/Charts/Classes/Charts/ChartViewBase.swift @@ -0,0 +1,738 @@ +// +// ChartViewBase.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// +// Based on https://github.com/PhilJay/MPAndroidChart/commit/c42b880 + +import Foundation +import UIKit; + +@objc +public protocol ChartViewDelegate +{ + /// Called when a value has been selected inside the chart. + /// :entry: The selected Entry. + /// :dataSetIndex: The index in the datasets array of the data object the Entrys DataSet is in. + optional func chartValueSelected(chartView: ChartViewBase, entry: ChartDataEntry, dataSetIndex: Int, highlight: ChartHighlight); + + // Called when nothing has been selected or an "un-select" has been made. + optional func chartValueNothingSelected(chartView: ChartViewBase); +} + +public class ChartViewBase: UIView, ChartAnimatorDelegate +{ + // MARK: - Properties + + /// custom formatter that is used instead of the auto-formatter if set + internal var _valueFormatter = NSNumberFormatter() + + /// the default value formatter + internal var _defaultValueFormatter = NSNumberFormatter() + + /// object that holds all data that was originally set for the chart, before it was modified or any filtering algorithms had been applied + internal var _data: ChartData! + + /// font object used for drawing the description text in the bottom right corner of the chart + public var descriptionFont: UIFont? = UIFont(name: "HelveticaNeue", size: 9.0) + internal var _descriptionTextColor: UIColor! = UIColor.blackColor() + + /// font object for drawing the information text when there are no values in the chart + internal var _infoFont: UIFont! = UIFont(name: "HelveticaNeue", size: 12.0) + internal var _infoTextColor: UIColor! = UIColor(red: 247.0/255.0, green: 189.0/255.0, blue: 51.0/255.0, alpha: 1.0) // orange + + /// description text that appears in the bottom right corner of the chart + public var descriptionText = "Description" + + /// flag that indicates if the chart has been fed with data yet + internal var _dataNotSet = true + + /// if true, units are drawn next to the values in the chart + internal var _drawUnitInChart = false + + /// the number of x-values the chart displays + internal var _deltaX = CGFloat(1.0) + + internal var _chartXMin = Float(0.0) + internal var _chartXMax = Float(0.0) + + /// if true, value highlightning is enabled + public var highlightEnabled = true + + /// the legend object containing all data associated with the legend + internal var _legend: ChartLegend! = ChartLegend(); + + /// delegate to receive chart events + public weak var delegate: ChartViewDelegate? + + /// text that is displayed when the chart is empty + public var noDataText = "No chart data available." + + /// text that is displayed when the chart is empty that describes why the chart is empty + public var noDataTextDescription: String? + + internal var _legendRenderer: ChartLegendRenderer! + + /// object responsible for rendering the data + public var renderer: ChartDataRendererBase? + + /// object that manages the bounds and drawing constraints of the chart + internal var _viewPortHandler: ChartViewPortHandler! + + /// object responsible for animations + internal var _animator: ChartAnimator! + + /// flag that indicates if offsets calculation has already been done or not + private var _offsetsCalculated = false + + /// array of Highlight objects that reference the highlighted slices in the chart + internal var _indicesToHightlight = [ChartHighlight]() + + /// if set to true, the marker is drawn when a value is clicked + public var drawMarkers = true + + /// the view that represents the marker + public var marker: ChartMarker? + + private var _interceptTouchEvents = false + + // MARK: - Initializers + + public override init(frame: CGRect) + { + super.init(frame: frame); + initialize(); + } + + public required init(coder aDecoder: NSCoder) + { + super.init(coder: aDecoder); + initialize(); + } + + internal func initialize() + { + _animator = ChartAnimator(); + _animator.delegate = self; + + _viewPortHandler = ChartViewPortHandler(); + _viewPortHandler.setChartDimens(width: bounds.size.width, height: bounds.size.height); + _legendRenderer = ChartLegendRenderer(viewPortHandler: _viewPortHandler); + + _defaultValueFormatter.maximumFractionDigits = 1; + _defaultValueFormatter.minimumFractionDigits = 1; + _defaultValueFormatter.usesGroupingSeparator = true; + + _valueFormatter = _defaultValueFormatter.copy() as! NSNumberFormatter; + } + + // MARK: - ChartViewBase + + /// The data for the chart + public var data: ChartData? + { + get + { + return _data; + } + set + { + if (newValue == nil) + { + println("Charts: data argument is nil on setData()"); + return; + } + + _dataNotSet = false; + _offsetsCalculated = false; + _data = newValue; + + // calculate how many digits are needed + calculateFormatter(min: _data.getYMin(), max: _data.getYMax()); + + notifyDataSetChanged(); + } + } + + /// Clears the chart from all data (sets it to null) and refreshes it (by calling setNeedsDisplay()). + public func clear() + { + _data = nil; + _dataNotSet = true; + setNeedsDisplay(); + } + + /// Removes all DataSets (and thereby Entries) from the chart. Does not remove the x-values. Also refreshes the chart by calling setNeedsDisplay(). + public func clearValues() + { + if (_data !== nil) + { + _data.clearValues(); + } + setNeedsDisplay(); + } + + /// Returns true if the chart is empty (meaning it's data object is either null or contains no entries). + public func isEmpty() -> Bool + { + if (_data == nil) + { + return true; + } + else + { + + if (_data.yValCount <= 0) + { + return true; + } + else + { + return false; + } + } + } + + /// Lets the chart know its underlying data has changed and should perform all necessary recalculations. + public func notifyDataSetChanged() + { + fatalError("notifyDataSetChanged() cannot be called on ChartViewBase"); + } + + /// calculates the offsets of the chart to the border depending on the position of an eventual legend or depending on the length of the y-axis and x-axis labels and their position + internal func calculateOffsets() + { + fatalError("calculateOffsets() cannot be called on ChartViewBase"); + } + + /// calcualtes the y-min and y-max value and the y-delta and x-delta value + internal func calcMinMax() + { + fatalError("calcMinMax() cannot be called on ChartViewBase"); + } + + /// calculates the required number of digits for the values that might be drawn in the chart (if enabled), and creates the default value formatter + internal func calculateFormatter(#min: Float, max: Float) + { + // check if a custom formatter is set or not + var reference = Float(0.0); + + if (_data == nil || _data.xValCount < 2) + { + var absMin = fabs(min); + var absMax = fabs(max); + reference = absMin > absMax ? absMin : absMax; + } + else + { + reference = fabs(max - min); + } + + var digits = ChartUtils.decimals(reference); + + _defaultValueFormatter.maximumFractionDigits = digits; + _defaultValueFormatter.minimumFractionDigits = digits; + } + + public override func drawRect(rect: CGRect) + { + let context = UIGraphicsGetCurrentContext(); + let frame = self.bounds; + + if (_dataNotSet) + { // check if there is data + + CGContextSaveGState(context); + + // if no data, inform the user + + ChartUtils.drawText(context: context, text: noDataText, point: CGPoint(x: frame.width / 2.0, y: frame.height / 2.0), align: .Center, attributes: [NSFontAttributeName: _infoFont, NSForegroundColorAttributeName: _infoTextColor]); + + if (noDataTextDescription!.lengthOfBytesUsingEncoding(NSUTF16StringEncoding) > 0) + { + var textOffset = -_infoFont.lineHeight / 2.0; + + ChartUtils.drawText(context: context, text: noDataTextDescription!, point: CGPoint(x: frame.width / 2.0, y: frame.height / 2.0 + textOffset), align: .Center, attributes: [NSFontAttributeName: _infoFont, NSForegroundColorAttributeName: _infoTextColor]); + } + + return; + } + + if (!_offsetsCalculated) + { + calculateOffsets(); + _offsetsCalculated = true; + } + } + + /// draws the description text in the bottom right corner of the chart + internal func drawDescription(#context: CGContext) + { + if (descriptionText.lengthOfBytesUsingEncoding(NSUTF16StringEncoding) == 0) + { + return; + } + + let frame = self.bounds; + + var attrs = [NSObject: AnyObject](); + + var font = descriptionFont; + + if (font == nil) + { + font = UIFont.systemFontOfSize(UIFont.systemFontSize()); + } + + attrs[NSFontAttributeName] = font; + attrs[NSForegroundColorAttributeName] = UIColor.blackColor(); + + ChartUtils.drawText(context: context, text: descriptionText, point: CGPoint(x: frame.width - _viewPortHandler.offsetRight - 10.0, y: frame.height - _viewPortHandler.offsetBottom - 10.0 - font!.lineHeight), align: .Right, attributes: attrs); + } + + /// disables intercept touchevents + public func disableScroll() + { + _interceptTouchEvents = true; + } + + /// enables intercept touchevents + public func enableScroll() + { + _interceptTouchEvents = false; + } + + // MARK: - Highlighting + + /// Returns the array of currently highlighted values. This might be null or empty if nothing is highlighted. + public var highlighted: [ChartHighlight] + { + return _indicesToHightlight; + } + + /// Returns true if there are values to highlight, + /// false if there are no values to highlight. + /// Checks if the highlight array is null, has a length of zero or if the first object is null. + public func valuesToHighlight() -> Bool + { + return _indicesToHightlight.count > 0; + } + + /// Highlights the values at the given indices in the given DataSets. Provide + /// null or an empty array to undo all highlighting. + /// This should be used to programmatically highlight values. + /// This DOES NOT generate a callback to the delegate. + public func highlightValues(highs: [ChartHighlight]?) + { + // set the indices to highlight + _indicesToHightlight = highs ?? [ChartHighlight](); + + // redraw the chart + setNeedsDisplay(); + } + + /// Highlights the value at the given x-index in the given DataSet. + /// Provide -1 as the x-index to undo all highlighting. + public func highlightValue(#xIndex: Int, dataSetIndex: Int, callDelegate: Bool) + { + if (xIndex < 0 || dataSetIndex < 0 || xIndex >= _data.xValCount || dataSetIndex >= _data.dataSetCount) + { + highlightValue(highlight: nil, callDelegate: callDelegate); + } + else + { + highlightValue(highlight: ChartHighlight(xIndex: xIndex, dataSetIndex: dataSetIndex), callDelegate: callDelegate); + } + } + + /// Highlights the value selected by touch gesture. + public func highlightValue(#highlight: ChartHighlight?, callDelegate: Bool) + { + if (highlight == nil) + { + _indicesToHightlight.removeAll(keepCapacity: false); + } + else + { + // set the indices to highlight + _indicesToHightlight = [highlight!]; + } + + // redraw the chart + setNeedsDisplay(); + + if (callDelegate && delegate != nil) + { + if (highlight == nil) + { + delegate!.chartValueNothingSelected!(self); + } + else + { + var e = _data.getEntryForHighlight(highlight!); + + // notify the listener + delegate!.chartValueSelected!(self, entry: e, dataSetIndex: highlight!.dataSetIndex, highlight: highlight!); + } + } + } + + // MARK: - Markers + + /// draws all MarkerViews on the highlighted positions + internal func drawMarkers(#context: CGContext) + { + // if there is no marker view or drawing marker is disabled + if (marker === nil || !drawMarkers || !valuesToHighlight()) + { + return; + } + + for (var i = 0, count = _indicesToHightlight.count; i < count; i++) + { + let highlight = _indicesToHightlight[i]; + let xIndex = highlight.xIndex; + let dataSetIndex = highlight.dataSetIndex; + + if (xIndex <= Int(_deltaX) && xIndex <= Int(_deltaX * _animator.phaseX)) + { + let e = _data.getEntryForHighlight(highlight); + + var pos = getMarkerPosition(entry: e, dataSetIndex: dataSetIndex); + + // check bounds + if (!_viewPortHandler.isInBounds(x: pos.x, y: pos.y)) + { + continue; + } + + // callbacks to update the content + marker!.refreshContent(entry: e, dataSetIndex: dataSetIndex); + + let markerSize = marker!.size; + if (pos.y - markerSize.height <= 0.0) + { + let y = markerSize.height - pos.y; + marker!.draw(context: context, point: CGPoint(x: pos.x, y: pos.y + y)); + } + else + { + marker!.draw(context: context, point: pos); + } + } + } + } + + /// Returns the actual position in pixels of the MarkerView for the given Entry in the given DataSet. + public func getMarkerPosition(#entry: ChartDataEntry, dataSetIndex: Int) -> CGPoint + { + fatalError("getMarkerPosition() cannot be called on ChartViewBase"); + } + + // MARK: - Animation + + /// Returns the animator responsible for animating chart values. + public var animator: ChartAnimator! + { + return _animator; + } + + /// Animates the drawing / rendering of the chart on both x- and y-axis with + /// the specified animation time. + /// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart. + public func animateXY(#durationX: NSTimeInterval, durationY: NSTimeInterval) + { + _animator.animateXY(durationX: durationX, durationY: durationY); + } + + /// Animates the drawing / rendering of the chart the x-axis with + /// the specified animation time. + /// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart. + public func animateX(#duration: NSTimeInterval) + { + _animator.animateX(duration: duration); + } + + /// Animates the drawing / rendering of the chart the y-axis with + /// the specified animation time. + /// If animate(...) is called, no further calling of invalidate() is necessary to refresh the chart. + public func animateY(#duration: NSTimeInterval) + { + _animator.animateY(duration: duration); + } + + // MARK: - Accessors + + /// returns the total value (sum) of all y-values across all DataSets + public var yValueSum: Float + { + return _data.yValueSum; + } + + /// returns the current y-max value across all DataSets + public var chartYMax: Float + { + return _data.yMax; + } + + /// returns the current y-min value across all DataSets + public var chartYMin: Float + { + return _data.yMin; + } + + public var chartXMax: Float + { + return _chartXMax; + } + + public var chartXMin: Float + { + return _chartXMin; + } + + /// returns the average value of all values the chart holds + public func getAverage() -> Float + { + return yValueSum / Float(_data.yValCount); + } + + /// returns the average value for a specific DataSet (with a specific label) in the chart + public func getAverage(#dataSetLabel: String) -> Float + { + var ds = _data.getDataSetByLabel(dataSetLabel, ignorecase: true); + if (ds == nil) + { + return 0.0; + } + + return ds!.yValueSum / Float(ds!.entryCount); + } + + /// returns the total number of values the chart holds (across all DataSets) + public var getValueCount: Int + { + return _data.yValCount; + } + + /// Returns the center of the chart taking offsets under consideration. (returns the center of the content rectangle) + public var centerOffsets: CGPoint + { + return _viewPortHandler.contentCenter; + } + + /// Returns the Legend object of the chart. + /// This property can be used to customize the automatically generated Legend. + /// IMPORTANT: Since the Legend is generated from data provided by the user (via setData(...) method), + /// this will return nil if no data has been set for the chart. + /// You need to set data for the chart before calling this method. + public var legend: ChartLegend + { + return _legend; + } + + /// Returns the renderer object responsible for rendering / drawing the Legend. + public var legendRenderer: ChartLegendRenderer! + { + return _legendRenderer; + } + + /// Returns the rectangle that defines the borders of the chart-value surface (into which the actual values are drawn). + public var contentRect: CGRect + { + return _viewPortHandler.contentRect; + } + + /// Sets the formatter to be used for drawing the values inside the chart. + /// If no formatter is set, the chart will automatically determine a reasonable + /// formatting (concerning decimals) for all the values that are drawn inside + /// the chart. Set this to nil to re-enable auto formatting. + public var valueFormatter: NSNumberFormatter! + { + get + { + return _valueFormatter; + } + set + { + if (newValue === nil) + { + _valueFormatter = _defaultValueFormatter.copy() as! NSNumberFormatter; + } + else + { + _valueFormatter = newValue; + } + } + } + + /// returns the x-value at the given index + public func getXValue(index: Int) -> String! + { + if (_data == nil || _data.xValCount <= index) + { + return nil; + } + else + { + return _data.xVals[index]; + } + } + + /// Get all Entry objects at the given index across all DataSets. + public func getEntriesAtIndex(xIndex: Int) -> [ChartDataEntry] + { + var vals = [ChartDataEntry](); + + for (var i = 0, count = _data.dataSetCount; i < count; i++) + { + var set = _data.getDataSetByIndex(i); + var e = set!.entryForXIndex(xIndex); + if (e !== nil) + { + vals.append(e); + } + } + + return vals; + } + + /// returns the percentage the given value has of the total y-value sum + public func percentOfTotal(val: Float) -> Float + { + return val / _data.yValueSum * 100.0; + } + + /// Returns the ViewPortHandler of the chart that is responsible for the + /// content area of the chart and its offsets and dimensions. + public var viewPortHandler: ChartViewPortHandler! + { + return _viewPortHandler; + } + + /// Returns the bitmap that represents the chart. + public func getChartImage() -> UIImage + { + UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, UIScreen.mainScreen().scale); + + layer.renderInContext(UIGraphicsGetCurrentContext()); + + var image = UIGraphicsGetImageFromCurrentImageContext(); + + UIGraphicsEndImageContext(); + + return image; + } + + public enum ImageFormat + { + case JPEG; + case PNG; + } + + /// Saves the current chart state with the given name to the given path on + /// the sdcard leaving the path empty "" will put the saved file directly on + /// the SD card chart is saved as a PNG image, example: + /// saveToPath("myfilename", "foldername1/foldername2"); + /// + /// :filePath: path to the image to save + /// :format: the format to save + /// :compressionQuality: compression quality for lossless formats (JPEG) + /// + /// :returns: true if the image was saved successfully + public func saveToPath(path: String, format: ImageFormat, compressionQuality: Float) -> Bool + { + var image = getChartImage(); + + var imageData: NSData!; + switch (format) + { + case .PNG: + imageData = UIImagePNGRepresentation(image); + break; + + case .JPEG: + imageData = UIImageJPEGRepresentation(image, CGFloat(compressionQuality)); + break; + } + + return imageData.writeToFile(path, atomically: true); + } + + /// Saves the current state of the chart to the camera roll + public func saveToCameraRoll() + { + UIImageWriteToSavedPhotosAlbum(getChartImage(), nil, nil, nil); + } + + public override var bounds: CGRect + { + get + { + return super.bounds; + } + set + { + super.bounds = newValue; + + if (_viewPortHandler !== nil) + { + _viewPortHandler.setChartDimens(width: newValue.size.width, height: newValue.size.height); + } + + notifyDataSetChanged(); + } + } + + /// if true, value highlightning is enabled + public var isHighlightEnabled: Bool { return highlightEnabled; } + + // MARK: - ChartAnimatorDelegate + + public func chartAnimatorUpdated(chartAnimator: ChartAnimator) + { + setNeedsDisplay(); + } + + // MARK: - Touches + + public override func touchesBegan(touches: Set, withEvent event: UIEvent) + { + if (!_interceptTouchEvents) + { + super.touchesBegan(touches, withEvent: event); + } + } + + public override func touchesMoved(touches: Set, withEvent event: UIEvent) + { + if (!_interceptTouchEvents) + { + super.touchesMoved(touches, withEvent: event); + } + } + + public override func touchesEnded(touches: Set, withEvent event: UIEvent) + { + if (!_interceptTouchEvents) + { + super.touchesEnded(touches, withEvent: event); + } + } + + public override func touchesCancelled(touches: Set, withEvent event: UIEvent) + { + if (!_interceptTouchEvents) + { + super.touchesCancelled(touches, withEvent: event); + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Charts/CombinedChartView.swift b/Charts/Classes/Charts/CombinedChartView.swift new file mode 100644 index 0000000000..eb48ebcf28 --- /dev/null +++ b/Charts/Classes/Charts/CombinedChartView.swift @@ -0,0 +1,186 @@ +// +// CombinedChartView.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// This chart class allows the combination of lines, bars, scatter and candle data all displayed in one chart area. +public class CombinedChartView: BarLineChartViewBase +{ + /// the fill-formatter used for determining the position of the fill-line + internal var _fillFormatter: ChartFillFormatter! + + /// enum that allows to specify the order in which the different data objects for the combined-chart are drawn + @objc + public enum DrawOrder: Int + { + case Bar + case Line + case Candle + case Scatter + } + + public override func initialize() + { + super.initialize(); + + _fillFormatter = BarLineChartFillFormatter(chart: self); + + renderer = CombinedChartRenderer(chart: self, animator: _animator, viewPortHandler: _viewPortHandler); + } + + override func calcMinMax() + { + super.calcMinMax(); + + if (self.barData !== nil || self.candleData !== nil) + { + _chartXMin = -0.5; + _chartXMax = Float(_data.xVals.count) - 0.5; + _deltaX = CGFloat(abs(_chartXMax - _chartXMin)); + } + } + + public override var data: ChartData? + { + get + { + return super.data; + } + set + { + super.data = newValue; + (renderer as! CombinedChartRenderer?)?.createRenderers(); + } + } + + public var fillFormatter: ChartFillFormatter + { + get + { + return _fillFormatter; + } + set + { + _fillFormatter = newValue; + if (_fillFormatter === nil) + { + _fillFormatter = BarLineChartFillFormatter(chart: self); + } + } + } + + public var lineData: LineChartData! + { + get + { + if (_data === nil) + { + return nil; + } + return (_data as! CombinedChartData!).lineData; + } + } + + public var barData: BarChartData! + { + get + { + if (_data === nil) + { + return nil; + } + return (_data as! CombinedChartData!).barData; + } + } + + public var scatterData: ScatterChartData! + { + get + { + if (_data === nil) + { + return nil; + } + return (_data as! CombinedChartData!).scatterData; + } + } + + public var candleData: CandleChartData! + { + get + { + if (_data === nil) + { + return nil; + } + return (_data as! CombinedChartData!).candleData; + } + } + + // MARK: Accessors + + /// flag that enables or disables the highlighting arrow + public var drawHighlightArrowEnabled: Bool + { + get { return (renderer as! CombinedChartRenderer!).drawHighlightArrowEnabled; } + set { (renderer as! CombinedChartRenderer!).drawHighlightArrowEnabled = newValue; } + } + + /// if set to true, all values are drawn above their bars, instead of below their top + public var drawValueAboveBarEnabled: Bool + { + get { return (renderer as! CombinedChartRenderer!).drawValueAboveBarEnabled; } + set { (renderer as! CombinedChartRenderer!).drawValueAboveBarEnabled = newValue; } + } + + /// if set to true, all values of a stack are drawn individually, and not just their sum + public var drawValuesForWholeStackEnabled: Bool + { + get { return (renderer as! CombinedChartRenderer!).drawValuesForWholeStackEnabled; } + set { (renderer as! CombinedChartRenderer!).drawValuesForWholeStackEnabled = newValue; } + } + + /// if set to true, a grey area is darawn behind each bar that indicates the maximum value + public var drawBarShadowEnabled: Bool + { + get { return (renderer as! CombinedChartRenderer!).drawBarShadowEnabled; } + set { (renderer as! CombinedChartRenderer!).drawBarShadowEnabled = newValue; } + } + + /// returns true if drawing the highlighting arrow is enabled, false if not + public var isDrawHighlightArrowEnabled: Bool { return (renderer as! CombinedChartRenderer!).drawHighlightArrowEnabled; } + + /// returns true if drawing values above bars is enabled, false if not + public var isDrawValueAboveBarEnabled: Bool { return (renderer as! CombinedChartRenderer!).drawValueAboveBarEnabled; } + + /// returns true if all values of a stack are drawn, and not just their sum + public var isDrawValuesForWholeStackEnabled: Bool { return (renderer as! CombinedChartRenderer!).drawValuesForWholeStackEnabled; } + + /// returns true if drawing shadows (maxvalue) for each bar is enabled, false if not + public var isDrawBarShadowEnabled: Bool { return (renderer as! CombinedChartRenderer!).drawBarShadowEnabled; } + + /// the order in which the provided data objects should be drawn. + /// The earlier you place them in the provided array, the further they will be in the background. + /// e.g. if you provide [DrawOrder.Bar, DrawOrder.Line], the bars will be drawn behind the lines. + public var drawOrder: [Int] + { + get + { + return (renderer as! CombinedChartRenderer!).drawOrder.map { $0.rawValue }; + } + set + { + (renderer as! CombinedChartRenderer!).drawOrder = newValue.map { DrawOrder(rawValue: $0)! }; + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Charts/HorizontalBarChartView.swift b/Charts/Classes/Charts/HorizontalBarChartView.swift new file mode 100644 index 0000000000..762b32d236 --- /dev/null +++ b/Charts/Classes/Charts/HorizontalBarChartView.swift @@ -0,0 +1,164 @@ +// +// HorizontalBarChartView.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// BarChart with horizontal bar orientation. In this implementation, x- and y-axis are switched. +public class HorizontalBarChartView: BarChartView +{ + internal override func initialize() + { + super.initialize(); + + renderer = HorizontalBarChartRenderer(delegate: self, animator: _animator, viewPortHandler: _viewPortHandler); + _leftYAxisRenderer = ChartYAxisRendererHorizontalBarChart(viewPortHandler: _viewPortHandler, yAxis: _leftAxis, transformer: _leftAxisTransformer); + _rightYAxisRenderer = ChartYAxisRendererHorizontalBarChart(viewPortHandler: _viewPortHandler, yAxis: _rightAxis, transformer: _rightAxisTransformer); + _xAxisRenderer = ChartXAxisRendererHorizontalBarChart(viewPortHandler: _viewPortHandler, xAxis: _xAxis, transformer: _leftAxisTransformer, chart: self); + } + + internal override func calculateOffsets() + { + var offsetLeft: CGFloat = 0.0, + offsetRight: CGFloat = 0.0, + offsetTop: CGFloat = 0.0, + offsetBottom: CGFloat = 0.0; + + // setup offsets for legend + if (_legend !== nil && _legend.isEnabled) + { + if (_legend.position == .RightOfChart + || _legend.position == .RightOfChartCenter) + { + offsetRight += _legend.textWidthMax + _legend.xOffset * 2.0; + } + else if (_legend.position == .BelowChartLeft + || _legend.position == .BelowChartRight + || _legend.position == .BelowChartCenter) + { + + offsetBottom += _legend.textHeightMax * 3.0; + } + } + + // offsets for y-labels + if (_leftAxis.needsOffset) + { + offsetTop += _leftAxis.requiredSize().height; + } + + if (_rightAxis.needsOffset) + { + offsetBottom += _rightAxis.requiredSize().height; + } + + var xlabelwidth = _xAxis.labelWidth; + + if (_xAxis.isEnabled) + { + // offsets for x-labels + if (_xAxis.labelPosition == .Bottom) + { + offsetLeft += xlabelwidth; + } + else if (_xAxis.labelPosition == .Top) + { + offsetRight += xlabelwidth; + } + else if (_xAxis.labelPosition == .BothSided) + { + offsetLeft += xlabelwidth; + offsetRight += xlabelwidth; + } + } + + var min: CGFloat = 10.0; + + _viewPortHandler.restrainViewPort( + offsetLeft: max(min, offsetLeft), + offsetTop: max(min, offsetTop), + offsetRight: max(min, offsetRight), + offsetBottom: max(min, offsetBottom)); + + prepareOffsetMatrix(); + prepareValuePxMatrix(); + } + + internal override func prepareValuePxMatrix() + { + _rightAxisTransformer.prepareMatrixValuePx(chartXMin: _rightAxis.axisMinimum, deltaX: CGFloat(_rightAxis.axisRange), deltaY: _deltaX, chartYMin: _chartXMin); + _leftAxisTransformer.prepareMatrixValuePx(chartXMin: _leftAxis.axisMinimum, deltaX: CGFloat(_leftAxis.axisRange), deltaY: _deltaX, chartYMin: _chartXMin); + } + + internal override func calcModulus() + { + _xAxis.axisLabelModulus = Int(ceil((CGFloat(_data.xValCount) * _xAxis.labelHeight) / (_viewPortHandler.contentHeight * viewPortHandler.touchMatrix.d))); + + if (_xAxis.axisLabelModulus < 1) + { + _xAxis.axisLabelModulus = 1; + } + } + + public override func getBarBounds(e: BarChartDataEntry) -> CGRect! + { + var set = _data.getDataSetForEntry(e) as! BarChartDataSet!; + + if (set === nil) + { + return nil; + } + + var barspace = set.barSpace; + var y = CGFloat(e.value); + var x = CGFloat(e.xIndex); + + var spaceHalf = barspace / 2.0; + var top = x - 0.5 + spaceHalf; + var bottom = x + 0.5 - spaceHalf; + var left = y >= 0.0 ? y : 0.0; + var right = y <= 0.0 ? y : 0.0; + + var bounds = CGRect(x: left, y: top, width: right - left, height: bottom - top); + + getTransformer(set.axisDependency).rectValueToPixel(&bounds); + + return bounds; + } + + public override func getPosition(e: ChartDataEntry, axis: ChartYAxis.AxisDependency) -> CGPoint + { + var vals = CGPoint(x: CGFloat(e.value), y: CGFloat(e.xIndex)); + + getTransformer(axis).pointValueToPixel(&vals); + + return vals; + } + + public override func getHighlightByTouchPoint(var pt: CGPoint) -> ChartHighlight! + { + if (_dataNotSet || _data === nil) + { + println("Can't select by touch. No data set."); + return nil; + } + + _leftAxisTransformer.pixelToValue(&pt); + + if (pt.y < CGFloat(_chartXMin) || pt.y > CGFloat(_chartXMax)) + { + return nil; + } + + return getHighlight(xPosition: pt.y, yPosition: pt.x); + } +} \ No newline at end of file diff --git a/Charts/Classes/Charts/LineChartView.swift b/Charts/Classes/Charts/LineChartView.swift new file mode 100644 index 0000000000..8603d803b9 --- /dev/null +++ b/Charts/Classes/Charts/LineChartView.swift @@ -0,0 +1,109 @@ +// +// LineChartView.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// Chart that draws lines, surfaces, circles, ... +public class LineChartView: BarLineChartViewBase, LineChartRendererDelegate +{ + /// the width of the highlighning line + /// :default: 3.0 + internal var highlightWidth: CGFloat = 3.0 + + private var _fillFormatter: ChartFillFormatter! + + internal override func initialize() + { + super.initialize(); + + renderer = LineChartRenderer(delegate: self, animator: _animator, viewPortHandler: _viewPortHandler); + + _fillFormatter = BarLineChartFillFormatter(chart: self); + } + + internal override func calcMinMax() + { + super.calcMinMax(); + + if (_deltaX == 0.0 && _data.yValCount > 0) + { + _deltaX = 1.0; + } + } + + public var fillFormatter: ChartFillFormatter! + { + get + { + return _fillFormatter; + } + set + { + if (newValue === nil) + { + _fillFormatter = BarLineChartFillFormatter(chart: self); + } + else + { + _fillFormatter = newValue; + } + } + } + + // MARK: - LineChartRendererDelegate + + public func lineChartRendererData(renderer: LineChartRenderer) -> LineChartData! + { + return _data as! LineChartData!; + } + + public func lineChartRenderer(renderer: LineChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer! + { + return self.getTransformer(which); + } + + public func lineChartRendererFillFormatter(renderer: LineChartRenderer) -> ChartFillFormatter + { + return self.fillFormatter; + } + + public func lineChartDefaultRendererValueFormatter(renderer: LineChartRenderer) -> NSNumberFormatter! + { + return self._defaultValueFormatter; + } + + public func lineChartRendererChartYMax(renderer: LineChartRenderer) -> Float + { + return self.chartYMax; + } + + public func lineChartRendererChartYMin(renderer: LineChartRenderer) -> Float + { + return self.chartYMin; + } + + public func lineChartRendererChartXMax(renderer: LineChartRenderer) -> Float + { + return self.chartXMax; + } + + public func lineChartRendererChartXMin(renderer: LineChartRenderer) -> Float + { + return self.chartXMin; + } + + public func lineChartRendererMaxVisibleValueCount(renderer: LineChartRenderer) -> Int + { + return self.maxVisibleValueCount; + } +} \ No newline at end of file diff --git a/Charts/Classes/Charts/PieChartView.swift b/Charts/Classes/Charts/PieChartView.swift new file mode 100644 index 0000000000..df00f0536b --- /dev/null +++ b/Charts/Classes/Charts/PieChartView.swift @@ -0,0 +1,442 @@ +// +// PieChartView.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// View that represents a pie chart. Draws cake like slices. +public class PieChartView: PieRadarChartViewBase +{ + /// rect object that represents the bounds of the piechart, needed for drawing the circle + private var _circleBox = CGRect() + + /// array that holds the width of each pie-slice in degrees + private var _drawAngles = [CGFloat]() + + /// array that holds the absolute angle in degrees of each slice + private var _absoluteAngles = [CGFloat]() + + public override init(frame: CGRect) + { + super.init(frame: frame); + } + + public required init(coder aDecoder: NSCoder) + { + super.init(coder: aDecoder); + } + + internal override func initialize() + { + super.initialize(); + + renderer = PieChartRenderer(chart: self, animator: _animator, viewPortHandler: _viewPortHandler); + } + + public override func drawRect(rect: CGRect) + { + super.drawRect(rect); + + if (_dataNotSet) + { + return; + } + + let context = UIGraphicsGetCurrentContext(); + + renderer!.drawData(context: context); + + if (self.highlightEnabled && valuesToHighlight()) + { + renderer!.drawHighlighted(context: context, indices: _indicesToHightlight); + } + + renderer!.drawExtras(context: context); + + renderer!.drawValues(context: context); + + _legendRenderer.renderLegend(context: context, legend: _legend); + + drawDescription(context: context); + } + + internal override func calculateOffsets() + { + super.calculateOffsets(); + + // prevent nullpointer when no data set + if (_dataNotSet) + { + return; + } + + var radius = diameter / 2.0; + + var c = centerOffsets; + + // create the circle box that will contain the pie-chart (the bounds of the pie-chart) + _circleBox.origin.x = c.x - radius; + _circleBox.origin.y = c.y - radius; + _circleBox.size.width = radius * 2.0; + _circleBox.size.height = radius * 2.0; + } + + internal override func calcMinMax() + { + super.calcMinMax(); + + calcAngles(); + } + + public override func getMarkerPosition(#entry: ChartDataEntry, dataSetIndex: Int) -> CGPoint + { + /// PieChart does not support MarkerView + return CGPoint(x: 0.0, y: 0.0); + } + + /// calculates the needed angles for the chart slices + private func calcAngles() + { + _drawAngles = [CGFloat](); + _absoluteAngles = [CGFloat](); + + _drawAngles.reserveCapacity(_data.yValCount); + _absoluteAngles.reserveCapacity(_data.yValCount); + + var dataSets = _data.dataSets; + + var cnt = 0; + + for (var i = 0; i < _data.dataSetCount; i++) + { + var set = dataSets[i]; + var entries = set.yVals; + + for (var j = 0; j < entries.count; j++) + { + _drawAngles.append(calcAngle(abs(entries[j].value))); + + if (cnt == 0) + { + _absoluteAngles.append(_drawAngles[cnt]); + } + else + { + _absoluteAngles.append(_absoluteAngles[cnt - 1] + _drawAngles[cnt]); + } + + cnt++; + } + } + } + + /// checks if the given index in the given DataSet is set for highlighting or not + public func needsHighlight(#xIndex: Int, dataSetIndex: Int) -> Bool + { + // no highlight + if (!valuesToHighlight() || dataSetIndex < 0) + { + return false; + } + + for (var i = 0; i < _indicesToHightlight.count; i++) + { + // check if the xvalue for the given dataset needs highlight + if (_indicesToHightlight[i].xIndex == xIndex + && _indicesToHightlight[i].dataSetIndex == dataSetIndex) + { + return true; + } + } + + return false; + } + + /// calculates the needed angle for a given value + private func calcAngle(value: Float) -> CGFloat + { + return CGFloat(value) / CGFloat(_data.yValueSum) * 360.0; + } + + public override func indexForAngle(angle: CGFloat) -> Int + { + // take the current angle of the chart into consideration + var a = (angle - _rotationAngle + 360) % 360.0; + + for (var i = 0; i < _absoluteAngles.count; i++) + { + if (_absoluteAngles[i] > a) + { + return i; + } + } + + return -1; // return -1 if no index found + } + + /// Returns the index of the DataSet this x-index belongs to. + public func dataSetIndexForIndex(xIndex: Int) -> Int + { + var dataSets = _data.dataSets; + + for (var i = 0; i < dataSets.count; i++) + { + if (dataSets[i].entryForXIndex(xIndex) !== nil) + { + return i; + } + } + + return -1; + } + + /// returns an integer array of all the different angles the chart slices + /// have the angles in the returned array determine how much space (of 360°) + /// each slice takes + public var drawAngles: [CGFloat] + { + return _drawAngles; + } + + /// returns the absolute angles of the different chart slices (where the + /// slices end) + public var absoluteAngles: [CGFloat] + { + return _absoluteAngles; + } + + /// Sets the color for the hole that is drawn in the center of the PieChart (if enabled). + /// NOTE: Use holeTransparent with holeColor = nil to make the hole transparent. + public var holeColor: UIColor? + { + get + { + return (renderer as! PieChartRenderer).holeColor!; + } + set + { + (renderer as! PieChartRenderer).holeColor = newValue; + setNeedsDisplay(); + } + } + + /// Set the hole in the center of the PieChart transparent + public var holeTransparent: Bool + { + get + { + return (renderer as! PieChartRenderer).holeTransparent; + } + set + { + (renderer as! PieChartRenderer).holeTransparent = newValue; + setNeedsDisplay(); + } + } + + /// Returns true if the hole in the center of the PieChart is transparent, false if not. + public var isHoleTransparent: Bool + { + return (renderer as! PieChartRenderer).holeTransparent; + } + + /// true if the hole in the center of the pie-chart is set to be visible, false if not + public var drawHoleEnabled: Bool + { + get + { + return (renderer as! PieChartRenderer).drawHoleEnabled; + } + set + { + (renderer as! PieChartRenderer).drawHoleEnabled = newValue; + setNeedsDisplay(); + } + } + + /// :returns: true if the hole in the center of the pie-chart is set to be visible, false if not + public var isDrawHoleEnabled: Bool + { + get + { + return (renderer as! PieChartRenderer).drawHoleEnabled; + } + } + + /// the text that is displayed in the center of the pie-chart. By default, the text is "Total value + sum of all values" + public var centerText: String! + { + get + { + return (renderer as! PieChartRenderer).centerText; + } + set + { + (renderer as! PieChartRenderer).centerText = newValue; + setNeedsDisplay(); + } + } + + /// true if drawing the center text is enabled + public var drawCenterTextEnabled: Bool + { + get + { + return (renderer as! PieChartRenderer).drawCenterTextEnabled; + } + set + { + (renderer as! PieChartRenderer).drawCenterTextEnabled = newValue; + setNeedsDisplay(); + } + } + + /// :returns: true if drawing the center text is enabled + public var isDrawCenterTextEnabled: Bool + { + get + { + return (renderer as! PieChartRenderer).drawCenterTextEnabled; + } + } + + internal override var requiredBottomOffset: CGFloat + { + return _legend.font.pointSize * 4.0; + } + + internal override var requiredBaseOffset: CGFloat + { + return 0.0; + } + + public override var radius: CGFloat + { + return _circleBox.width / 2.0; + } + + /// returns the circlebox, the boundingbox of the pie-chart slices + public var circleBox: CGRect + { + return _circleBox; + } + + /// returns the center of the circlebox + public var centerCircleBox: CGPoint + { + return CGPoint(x: _circleBox.midX, y: _circleBox.midY); + } + + /// Sets the font of the center text of the piechart. + public var centerTextFont: UIFont + { + get + { + return (renderer as! PieChartRenderer).centerTextFont; + } + set + { + (renderer as! PieChartRenderer).centerTextFont = newValue; + setNeedsDisplay(); + } + } + + /// Sets the color of the center text of the piechart. + public var centerTextColor: UIColor + { + get + { + return (renderer as! PieChartRenderer).centerTextColor; + } + set + { + (renderer as! PieChartRenderer).centerTextColor = newValue; + setNeedsDisplay(); + } + } + + /// the radius of the hole in the center of the piechart in percent of the maximum radius (max = the radius of the whole chart) + /// :default: 0.5 (50%) (half the pie) + public var holeRadiusPercent: CGFloat + { + get + { + return (renderer as! PieChartRenderer).holeRadiusPercent; + } + set + { + (renderer as! PieChartRenderer).holeRadiusPercent = newValue; + setNeedsDisplay(); + } + } + + /// the radius of the transparent circle that is drawn next to the hole in the piechart in percent of the maximum radius (max = the radius of the whole chart) + /// :default: 0.55 (55%) -> means 5% larger than the center-hole by default + public var transparentCircleRadiusPercent: CGFloat + { + get + { + return (renderer as! PieChartRenderer).transparentCircleRadiusPercent; + } + set + { + (renderer as! PieChartRenderer).transparentCircleRadiusPercent = newValue; + setNeedsDisplay(); + } + } + + /// set this to true to draw the x-value text into the pie slices + public var drawSliceTextEnabled: Bool + { + get + { + return (renderer as! PieChartRenderer).drawXLabelsEnabled; + } + set + { + (renderer as! PieChartRenderer).drawXLabelsEnabled = newValue; + setNeedsDisplay(); + } + } + + /// :returns: true if drawing x-values is enabled, false if not + public var isDrawSliceTextEnabled: Bool + { + get + { + return (renderer as! PieChartRenderer).drawXLabelsEnabled; + } + } + + /// If this is enabled, values inside the PieChart are drawn in percent and not with their original value. Values provided for the ValueFormatter to format are then provided in percent. + public var usePercentValuesEnabled: Bool + { + get + { + return (renderer as! PieChartRenderer).usePercentValuesEnabled; + } + set + { + (renderer as! PieChartRenderer).usePercentValuesEnabled = newValue; + setNeedsDisplay(); + } + } + + /// :returns: true if drawing x-values is enabled, false if not + public var isUsePercentValuesEnabled: Bool + { + get + { + return (renderer as! PieChartRenderer).usePercentValuesEnabled; + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Charts/PieRadarChartViewBase.swift b/Charts/Classes/Charts/PieRadarChartViewBase.swift new file mode 100644 index 0000000000..4a92219d3e --- /dev/null +++ b/Charts/Classes/Charts/PieRadarChartViewBase.swift @@ -0,0 +1,504 @@ +// +// PieRadarChartViewBase.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// Base class of PieChartView and RadarChartView. +public class PieRadarChartViewBase: ChartViewBase +{ + /// holds the current rotation angle of the chart + internal var _rotationAngle = CGFloat(270.0) + + /// flag that indicates if rotation is enabled or not + public var rotationEnabled = true + + private var _tapGestureRecognizer: UITapGestureRecognizer! + + public override init(frame: CGRect) + { + super.init(frame: frame); + } + + public required init(coder aDecoder: NSCoder) + { + super.init(coder: aDecoder); + } + + deinit + { + spinAnimationLoop(); + } + + internal override func initialize() + { + super.initialize(); + + _tapGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("tapGestureRecognized:")); + self.addGestureRecognizer(_tapGestureRecognizer); + } + + internal override func calcMinMax() + { + _deltaX = CGFloat(_data.xVals.count - 1); + } + + public override func notifyDataSetChanged() + { + if (_dataNotSet) + { + return; + } + + calcMinMax(); + + _legend = _legendRenderer.computeLegend(_data, legend: _legend); + + calculateOffsets(); + + setNeedsDisplay(); + } + + internal override func calculateOffsets() + { + var legendRight = CGFloat(0.0); + var legendBottom = CGFloat(0.0); + var legendTop = CGFloat(0.0); + + if (_legend != nil && _legend.enabled) + { + if (_legend.position == .RightOfChartCenter) + { + // this is the space between the legend and the chart + var spacing = CGFloat(13.0); + + legendRight = self.fullLegendWidth + spacing; + } + else if (_legend.position == .RightOfChart) + { + + // this is the space between the legend and the chart + var spacing = CGFloat(8.0); + var legendWidth = self.fullLegendWidth + spacing; + var legendHeight = _legend.neededHeight + _legend.textHeightMax; + + var c = self.center; + + var bottomRight = CGPoint(x: self.bounds.width - legendWidth + 15.0, y: legendHeight + 15); + var distLegend = distanceToCenter(x: bottomRight.x, y: bottomRight.y); + + var reference = getPosition(center: c, dist: self.radius, + angle: angleForPoint(x: bottomRight.x, y: bottomRight.y)); + + var distReference = distanceToCenter(x: reference.x, y: reference.y); + var min = CGFloat(5.0); + + if (distLegend < distReference) + { + var diff = distReference - distLegend; + legendRight = min + diff; + } + + if (bottomRight.y >= c.y && self.bounds.height - legendWidth > self.bounds.width) + { + legendRight = legendWidth; + } + } + else if (_legend.position == .BelowChartLeft + || _legend.position == .BelowChartRight + || _legend.position == .BelowChartCenter) + { + legendBottom = self.requiredBottomOffset; + } + + legendRight += self.requiredBaseOffset; + legendTop += self.requiredBaseOffset; + } + + var min = CGFloat(10.0); + + var offsetLeft = max(min, self.requiredBaseOffset); + var offsetTop = max(min, legendTop); + var offsetRight = max(min, legendRight); + var offsetBottom = max(min, max(self.requiredBaseOffset, legendBottom)); + + _viewPortHandler.restrainViewPort(offsetLeft: offsetLeft, offsetTop: offsetTop, offsetRight: offsetRight, offsetBottom: offsetBottom); + } + + /// returns the angle relative to the chart center for the given point on the chart in degrees. + /// The angle is always between 0 and 360°, 0° is NORTH, 90° is EAST, ... + public func angleForPoint(#x: CGFloat, y: CGFloat) -> CGFloat + { + var c = centerOffsets; + + var tx = Double(x - c.x); + var ty = Double(y - c.y); + var length = sqrt(tx * tx + ty * ty); + var r = acos(ty / length); + + var angle = r * ChartUtils.Math.RAD2DEG; + + if (x > c.x) + { + angle = 360.0 - angle; + } + + // add 90° because chart starts EAST + angle = angle + 90.0; + + // neutralize overflow + if (angle > 360.0) + { + angle = angle - 360.0; + } + + return CGFloat(angle); + } + + /// Calculates the position around a center point, depending on the distance + /// from the center, and the angle of the position around the center. + internal func getPosition(#center: CGPoint, dist: CGFloat, angle: CGFloat) -> CGPoint + { + var a = cos(angle * ChartUtils.Math.FDEG2RAD); + return CGPoint(x: center.x + dist * cos(angle * ChartUtils.Math.FDEG2RAD), + y: center.y + dist * sin(angle * ChartUtils.Math.FDEG2RAD)); + } + + /// Returns the distance of a certain point on the chart to the center of the chart. + public func distanceToCenter(#x: CGFloat, y: CGFloat) -> CGFloat + { + var c = self.centerOffsets; + + var dist = CGFloat(0.0); + + var xDist = CGFloat(0.0); + var yDist = CGFloat(0.0); + + if (x > c.x) + { + xDist = x - c.x; + } + else + { + xDist = c.x - x; + } + + if (y > c.y) + { + yDist = y - c.y; + } + else + { + yDist = c.y - y; + } + + // pythagoras + dist = sqrt(pow(xDist, 2.0) + pow(yDist, 2.0)); + + return dist; + } + + /// Returns the xIndex for the given angle around the center of the chart. + /// Returns -1 if not found / outofbounds. + public func indexForAngle(angle: CGFloat) -> Int + { + fatalError("indexForAngle() cannot be called on PieRadarChartViewBase"); + } + + /// current rotation angle of the pie chart + /// :default: 270f --> top (NORTH) + public var rotationAngle: CGFloat + { + get + { + return _rotationAngle; + } + set + { + while (_rotationAngle < 0.0) + { + _rotationAngle += 360.0; + } + _rotationAngle = newValue % 360.0; + setNeedsDisplay(); + } + } + + /// returns the diameter of the pie- or radar-chart + public var diameter: CGFloat + { + var content = _viewPortHandler.contentRect; + return min(content.width, content.height); + } + + /// Returns the radius of the chart in pixels. + public var radius: CGFloat + { + fatalError("radius cannot be called on PieRadarChartViewBase"); + } + + /// Returns the required bottom offset for the chart. + internal var requiredBottomOffset: CGFloat + { + fatalError("requiredBottomOffset cannot be called on PieRadarChartViewBase"); + } + + /// Returns the base offset needed for the chart without calculating the + /// legend size. + internal var requiredBaseOffset: CGFloat + { + fatalError("requiredBaseOffset cannot be called on PieRadarChartViewBase"); + } + + /// Returns the required right offset for the chart. + private var fullLegendWidth: CGFloat + { + return _legend.textWidthMax + _legend.formSize + _legend.formToTextSpace; + } + + public override var chartXMax: Float + { + return 0.0; + } + + public override var chartXMin: Float + { + return 0.0; + } + + /// Returns an array of SelInfo objects for the given x-index. + /// The SelInfo objects give information about the value at the selected index and the DataSet it belongs to. + public func getYValsAtIndex(xIndex: Int) -> [ChartSelInfo] + { + var vals = [ChartSelInfo](); + + for (var i = 0; i < _data.dataSetCount; i++) + { + var dataSet = _data.getDataSetByIndex(i); + + // extract all y-values from all DataSets at the given x-index + var yVal = dataSet!.yValForXIndex(xIndex); + + if (!isnan(yVal)) + { + vals.append(ChartSelInfo(value: yVal, dataSetIndex: i, dataSet: dataSet!)); + } + } + + return vals; + } + + public var isRotationEnabled: Bool { return rotationEnabled; } + + // MARK: - Animation + + private var _spinDisplayLink: CADisplayLink!; + private var _spinFromAngle: CGFloat = 0.0; + private var _spinToAngle: CGFloat = 0.0; + private var _spinStartTime: NSTimeInterval = 0.0; + private var _spinEndTime: NSTimeInterval = 0.0; + + /// Applys a spin animation to the Chart. + public func spin(duration: NSTimeInterval, fromAngle: CGFloat, toAngle: CGFloat) + { + if (_spinDisplayLink != nil) + { + _spinDisplayLink.removeFromRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes); + } + + self.rotationAngle = fromAngle; + + _spinDisplayLink = CADisplayLink(target: self, selector: Selector("spinAnimationLoop")); + _spinFromAngle = fromAngle; + _spinToAngle = toAngle; + _spinStartTime = CACurrentMediaTime(); + _spinEndTime = _spinStartTime + duration; + + _spinDisplayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes); + } + + public func stopSpinAnimation() + { + if (_spinDisplayLink != nil) + { + _spinDisplayLink.removeFromRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes); + _spinDisplayLink = nil; + } + } + + @objc private func spinAnimationLoop() + { + var currentTime = CACurrentMediaTime(); + var duration = _spinEndTime - _spinStartTime; + var value = duration == 0.0 ? 0.0 : CGFloat((currentTime - _spinStartTime) / duration); + if (value > 1.0) + { + value = 1.0; + } + + if (currentTime >= _spinEndTime) + { + stopSpinAnimation(); + } + + self.rotationAngle = (_spinToAngle - _spinFromAngle) * value + _spinFromAngle; + } + + // MARK: - Gestures + + private var _touchStartPoint: CGPoint!; + private var _isRotating = false; + private var _startAngle = CGFloat(0.0) + + public override func touchesBegan(touches: Set, withEvent event: UIEvent) + { + super.touchesBegan(touches, withEvent: event); + + // if rotation by touch is enabled + if (rotationEnabled) + { + var touch = touches.first as! UITouch!; + + var touchLocation = touch.locationInView(self); + _touchStartPoint = touchLocation; + + self.setStartAngle(x: touchLocation.x, y: touchLocation.y); + } + } + + public override func touchesMoved(touches: Set, withEvent event: UIEvent) + { + super.touchesMoved(touches, withEvent: event); + + if (rotationEnabled) + { + var touch = touches.first as! UITouch!; + + var touchLocation = touch.locationInView(self); + + if (!_isRotating && distance(eventX: touchLocation.x, startX: _touchStartPoint.x, eventY: touchLocation.y, startY: _touchStartPoint.y) > CGFloat(8.0)) + { + _isRotating = true; + self.disableScroll(); + } + else + { + self.updateRotation(x: touchLocation.x, y: touchLocation.y); + } + } + } + + public override func touchesEnded(touches: Set, withEvent event: UIEvent) + { + super.touchesEnded(touches, withEvent: event); + + if (rotationEnabled) + { + var touch = touches.first as! UITouch!; + + var touchLocation = touch.locationInView(self); + _touchStartPoint = touchLocation; + + self.enableScroll(); + _isRotating = false; + } + } + + /// returns the distance between two points + private func distance(#eventX: CGFloat, startX: CGFloat, eventY: CGFloat, startY: CGFloat) -> CGFloat + { + var dx = eventX - startX; + var dy = eventY - startY; + return sqrt(dx * dx + dy * dy); + } + + /// sets the starting angle of the rotation, this is only used by the touch listener, x and y is the touch position + private func setStartAngle(#x: CGFloat, y: CGFloat) + { + _startAngle = angleForPoint(x: x, y: y); + + // take the current angle into consideration when starting a new drag + _startAngle -= _rotationAngle; + + setNeedsDisplay(); + } + + /// updates the view rotation depending on the given touch position, also takes the starting angle into consideration + private func updateRotation(#x: CGFloat, y: CGFloat) + { + self.rotationAngle = angleForPoint(x: x, y: y) - _startAngle; + } + + /// reference to the last highlighted object + private var _lastHighlight: ChartHighlight!; + + @objc private func tapGestureRecognized(recognizer: UITapGestureRecognizer) + { + if (recognizer.state == UIGestureRecognizerState.Ended) + { + var location = recognizer.locationInView(self); + var distance = distanceToCenter(x: location.x, y: location.y); + + // check if a slice was touched + if (distance > self.radius) + { + // if no slice was touched, highlight nothing + self.highlightValues(nil); + _lastHighlight = nil; + _lastHighlight = nil; + } + else + { + var angle = angleForPoint(x: location.x, y: location.y); + + if (self.isKindOfClass(PieChartView)) + { + angle /= _animator.phaseY; + } + + var index = indexForAngle(angle); + + // check if the index could be found + if (index < 0) + { + self.highlightValues(nil); + _lastHighlight = nil; + } + else + { + var valsAtIndex = getYValsAtIndex(index); + + var dataSetIndex = 0; + + // get the dataset that is closest to the selection (PieChart only has one DataSet) + if (self.isKindOfClass(RadarChartView)) + { + dataSetIndex = ChartUtils.closestDataSetIndex(valsAtIndex, value: Float(distance / (self as! RadarChartView).factor), axis: nil); + } + + var h = ChartHighlight(xIndex: index, dataSetIndex: dataSetIndex); + + if (_lastHighlight !== nil && h == _lastHighlight) + { + self.highlightValue(highlight: nil, callDelegate: true); + _lastHighlight = nil; + } + else + { + self.highlightValue(highlight: h, callDelegate: true); + _lastHighlight = h; + } + } + } + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Charts/RadarChartView.swift b/Charts/Classes/Charts/RadarChartView.swift new file mode 100644 index 0000000000..166fedc25d --- /dev/null +++ b/Charts/Classes/Charts/RadarChartView.swift @@ -0,0 +1,240 @@ +// +// RadarChartView.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// Implementation of the RadarChart, a "spidernet"-like chart. It works best +/// when displaying 5-10 entries per DataSet. +public class RadarChartView: PieRadarChartViewBase +{ + /// width of the web lines that come from the center. + public var webLineWidth = CGFloat(1.5) + + /// width of the web lines that are in between the lines coming from the center + public var innerWebLineWidth = CGFloat(0.75) + + /// color for the web lines that come from the center + public var webColor = UIColor(red: 122/255.0, green: 122/255.0, blue: 122.0/255.0, alpha: 1.0) + + /// color for the web lines in between the lines that come from the center. + public var innerWebColor = UIColor(red: 122/255.0, green: 122/255.0, blue: 122.0/255.0, alpha: 1.0) + + /// transparency the grid is drawn with (0.0 - 1.0) + public var webAlpha: CGFloat = 150.0 / 255.0 + + /// flag indicating if the web lines should be drawn or not + public var drawWeb = true + + /// the object reprsenting the y-axis labels + private var _yAxis: ChartYAxis! + + /// the object representing the x-axis labels + private var _xAxis: ChartXAxis! + + internal var _yAxisRenderer: ChartYAxisRendererRadarChart! + internal var _xAxisRenderer: ChartXAxisRendererRadarChart! + + public override init(frame: CGRect) + { + super.init(frame: frame); + } + + public required init(coder aDecoder: NSCoder) + { + super.init(coder: aDecoder); + } + + internal override func initialize() + { + super.initialize(); + + _yAxis = ChartYAxis(position: .Left); + _xAxis = ChartXAxis(); + _xAxis.spaceBetweenLabels = 0; + + renderer = RadarChartRenderer(chart: self, animator: _animator, viewPortHandler: _viewPortHandler); + + _yAxisRenderer = ChartYAxisRendererRadarChart(viewPortHandler: _viewPortHandler, yAxis: _yAxis, chart: self); + _xAxisRenderer = ChartXAxisRendererRadarChart(viewPortHandler: _viewPortHandler, xAxis: _xAxis, chart: self); + } + + internal override func calcMinMax() + { + super.calcMinMax(); + + var minLeft = _data.getYMin(.Left); + var maxLeft = _data.getYMax(.Left); + + _chartXMax = Float(_data.xVals.count) - 1.0; + _deltaX = CGFloat(abs(_chartXMax - _chartXMin)); + + var leftRange = CGFloat(abs(maxLeft - (_yAxis.isStartAtZeroEnabled ? 0.0 : minLeft))); + + var topSpaceLeft = leftRange * _yAxis.spaceTop; + var bottomSpaceLeft = leftRange * _yAxis.spaceBottom; + + _chartXMax = Float(_data.xVals.count) - 1.0; + _deltaX = CGFloat(abs(_chartXMax - _chartXMin)); + + _yAxis.axisMaximum = !isnan(_yAxis.customAxisMax) ? _yAxis.customAxisMax : maxLeft + Float(topSpaceLeft); + _yAxis.axisMinimum = !isnan(_yAxis.customAxisMin) ? _yAxis.customAxisMin : minLeft - Float(bottomSpaceLeft); + + // consider starting at zero (0) + if (_yAxis.isStartAtZeroEnabled) + { + _yAxis.axisMinimum = 0.0; + } + + _yAxis.axisRange = abs(_yAxis.axisMaximum - _yAxis.axisMinimum); + } + + public override func getMarkerPosition(#entry: ChartDataEntry, dataSetIndex: Int) -> CGPoint + { + var angle = self.sliceAngle * CGFloat(entry.xIndex) + self.rotationAngle; + var val = CGFloat(entry.value) * self.factor; + var c = self.centerOffsets; + + var p = CGPoint(x: c.x + val * cos(angle * ChartUtils.Math.FDEG2RAD), + y: c.y + val * sin(angle * ChartUtils.Math.FDEG2RAD)); + + return p; + } + + public override func notifyDataSetChanged() + { + if (_dataNotSet) + { + return; + } + + calcMinMax(); + + _yAxis?._defaultValueFormatter = _defaultValueFormatter; + + _yAxisRenderer?.computeAxis(yMin: _yAxis.axisMinimum, yMax: _yAxis.axisMaximum); + _xAxisRenderer?.computeAxis(xValAverageLength: _data.xValAverageLength, xValues: _data.xVals); + + _legend = _legendRenderer?.computeLegend(_data, legend: _legend); + + calculateOffsets(); + + setNeedsDisplay(); + } + + public override func drawRect(rect: CGRect) + { + super.drawRect(rect); + + if (_dataNotSet) + { + return; + } + + let context = UIGraphicsGetCurrentContext(); + + _xAxisRenderer?.renderAxisLabels(context: context); + + if (drawWeb) + { + renderer!.drawExtras(context: context); + } + + _yAxisRenderer.renderLimitLines(context: context); + + renderer!.drawData(context: context); + + if (self.highlightEnabled && valuesToHighlight()) + { + renderer!.drawHighlighted(context: context, indices: _indicesToHightlight); + } + + _yAxisRenderer.renderAxisLabels(context: context); + + renderer!.drawValues(context: context); + + _legendRenderer.renderLegend(context: context, legend: _legend); + + drawDescription(context: context); + + drawMarkers(context: context); + } + + /// Returns the factor that is needed to transform values into pixels. + public var factor: CGFloat + { + var content = _viewPortHandler.contentRect; + return min(content.width / 2.0, content.height / 2.0) + / CGFloat(_yAxis.axisRange); + } + + /// Returns the angle that each slice in the radar chart occupies. + public var sliceAngle: CGFloat + { + return 360.0 / CGFloat(_data.xValCount); + } + + public override func indexForAngle(angle: CGFloat) -> Int + { + // take the current angle of the chart into consideration + var a = (angle - _rotationAngle + 360.0) % 360.0; + + var sliceAngle = self.sliceAngle; + + for (var i = 0; i < _data.xValCount; i++) + { + if (sliceAngle * CGFloat(i + 1) - sliceAngle / 2.0 > a) + { + return i; + } + } + + return 0; + } + + /// Returns the object that represents all y-labels of the RadarChart. + public var yAxis: ChartYAxis + { + return _yAxis; + } + + /// Returns the object that represents all x-labels that are placed around the RadarChart. + public var xAxis: ChartXAxis + { + return _xAxis; + } + + internal override var requiredBottomOffset: CGFloat + { + return _legend.font.pointSize * 6.5; + } + + internal override var requiredBaseOffset: CGFloat + { + return _xAxis.labelWidth; + } + + public override var radius: CGFloat + { + var content = _viewPortHandler.contentRect; + return min(content.width / 2.0, content.height / 2.0); + } + + /// Returns the maximum value this chart can display on it's y-axis. + public override var chartYMax: Float { return _yAxis.axisMaximum; } + + /// Returns the minimum value this chart can display on it's y-axis. + public override var chartYMin: Float { return _yAxis.axisMinimum; } + + /// Returns the range of y-values this chart can display. + public var yRange: Float { return _yAxis.axisRange} +} \ No newline at end of file diff --git a/Charts/Classes/Charts/ScatterChartView.swift b/Charts/Classes/Charts/ScatterChartView.swift new file mode 100644 index 0000000000..201ae17bca --- /dev/null +++ b/Charts/Classes/Charts/ScatterChartView.swift @@ -0,0 +1,81 @@ +// +// ScatterChartView.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// The ScatterChart. Draws dots, triangles, squares and custom shapes into the chartview. +public class ScatterChartView: BarLineChartViewBase, ScatterChartRendererDelegate +{ + public override func initialize() + { + super.initialize(); + + renderer = ScatterChartRenderer(delegate: self, animator: _animator, viewPortHandler: _viewPortHandler); + _chartXMin = -0.5; + } + + public override func calcMinMax() + { + super.calcMinMax(); + + if (_deltaX == 0.0 && _data.yValCount > 0) + { + _deltaX = 1.0; + } + + _chartXMax += 0.5; + _deltaX = CGFloat(abs(_chartXMax - _chartXMin)); + } + + // MARK: - ScatterChartRendererDelegate + + public func scatterChartRendererData(renderer: ScatterChartRenderer) -> ScatterChartData! + { + return _data as! ScatterChartData!; + } + + public func scatterChartRenderer(renderer: ScatterChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer! + { + return getTransformer(which); + } + + public func scatterChartDefaultRendererValueFormatter(renderer: ScatterChartRenderer) -> NSNumberFormatter! + { + return self._defaultValueFormatter; + } + + public func scatterChartRendererChartYMax(renderer: ScatterChartRenderer) -> Float + { + return self.chartYMax; + } + + public func scatterChartRendererChartYMin(renderer: ScatterChartRenderer) -> Float + { + return self.chartYMin; + } + + public func scatterChartRendererChartXMax(renderer: ScatterChartRenderer) -> Float + { + return self.chartXMax; + } + + public func scatterChartRendererChartXMin(renderer: ScatterChartRenderer) -> Float + { + return self.chartXMin; + } + + public func scatterChartRendererMaxVisibleValueCount(renderer: ScatterChartRenderer) -> Int + { + return self.maxVisibleValueCount; + } +} \ No newline at end of file diff --git a/Charts/Classes/Components/ChartAxisBase.swift b/Charts/Classes/Components/ChartAxisBase.swift new file mode 100644 index 0000000000..8de42d0184 --- /dev/null +++ b/Charts/Classes/Components/ChartAxisBase.swift @@ -0,0 +1,57 @@ +// +// ChartAxisBase.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit + +public class ChartAxisBase: ChartComponentBase +{ + public var labelFont = UIFont.systemFontOfSize(10.0) + public var labelTextColor = UIColor.blackColor() + + public var axisLineColor = UIColor.grayColor() + public var axisLineWidth = CGFloat(0.5) + public var axisLineDashPhase = CGFloat(0.0) + public var axisLineDashLengths: [CGFloat]! + + public var gridColor = UIColor.grayColor().colorWithAlphaComponent(0.9) + public var gridLineWidth = CGFloat(0.5) + public var gridLineDashPhase = CGFloat(0.0) + public var gridLineDashLengths: [CGFloat]! + + public var drawGridLinesEnabled = true + public var drawAxisLineEnabled = true + + /// flag that indicates of the labels of this axis should be drawn or not + public var drawLabelsEnabled = true + + public var xOffset = CGFloat(5.0) + public var yOffset = CGFloat(5.0) + + public override init() + { + super.init(); + } + + public func getLongestLabel() -> String + { + fatalError("getLongestLabel() cannot be called on ChartAxisBase"); + } + + public var isDrawGridLinesEnabled: Bool { return drawGridLinesEnabled; } + + public var isDrawAxisLineEnabled: Bool { return drawAxisLineEnabled; } + + public var isDrawLabelsEnabled: Bool { return drawLabelsEnabled; } +} \ No newline at end of file diff --git a/Charts/Classes/Components/ChartComponentBase.swift b/Charts/Classes/Components/ChartComponentBase.swift new file mode 100644 index 0000000000..32996ee3b5 --- /dev/null +++ b/Charts/Classes/Components/ChartComponentBase.swift @@ -0,0 +1,29 @@ +// +// ChartComponentBase.swift +// Charts +// +// Created by Daniel Cohen Gindi on 16/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit + +/// This class encapsulates everything both Axis and Legend have in common. +public class ChartComponentBase: NSObject +{ + /// flag that indicates if this component is enabled or not + public var enabled = true + + public override init() + { + super.init(); + } + + public var isEnabled: Bool { return enabled; } +} diff --git a/Charts/Classes/Components/ChartLegend.swift b/Charts/Classes/Components/ChartLegend.swift new file mode 100644 index 0000000000..97ffec4d66 --- /dev/null +++ b/Charts/Classes/Components/ChartLegend.swift @@ -0,0 +1,223 @@ +// +// ChartLegend.swift +// Charts +// +// Created by Daniel Cohen Gindi on 24/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit + +public class ChartLegend: ChartComponentBase +{ + @objc + public enum ChartLegendPosition: Int + { + case RightOfChart + case RightOfChartCenter + case RightOfChartInside + case BelowChartLeft + case BelowChartRight + case BelowChartCenter + case PiechartCenter + } + + @objc + public enum ChartLegendForm: Int + { + case Square + case Circle + case Line + } + + private var _colors = [UIColor?]() + private var _labels = [String?]() + + public var position = ChartLegendPosition.BelowChartLeft + + public var font: UIFont = UIFont.systemFontOfSize(10.0) + public var textColor = UIColor.blackColor() + + public var form = ChartLegendForm.Square + public var formSize = CGFloat(8.0) + public var formLineWidth = CGFloat(1.5) + + public var xEntrySpace = CGFloat(6.0) + public var yEntrySpace = CGFloat(5.0) + public var formToTextSpace = CGFloat(5.0) + public var stackSpace = CGFloat(3.0) + + public var xOffset = CGFloat(5.0) + public var yOffset = CGFloat(6.0) + + public override init() + { + super.init(); + } + + public init(colors: [UIColor?], labels: [String?]) + { + super.init(); + self._colors = colors; + self._labels = labels; + + validateLabelsAndColors(); + } + + public func getMaximumEntrySize(font: UIFont) -> CGSize + { + var maxW = CGFloat(0.0); + var maxH = CGFloat(0.0); + + for (var i = 0; i < _labels.count; i++) + { + if (_labels[i] == nil) + { + continue; + } + + var size = (_labels[i] as NSString!).sizeWithAttributes([NSFontAttributeName: font]); + + if (size.width > maxW) + { + maxW = size.width; + } + if (size.height > maxH) + { + maxH = size.height; + } + } + + return CGSize( + width: maxW + formSize + formToTextSpace, + height: maxH + ); + } + + public var colors: [UIColor?] + { + get + { + return _colors; + } + } + + public var labels: [String?] + { + get + { + return _labels; + } + set + { + _labels = newValue; + validateLabelsAndColors(); + } + } + + private func validateLabelsAndColors() + { + if (_labels.count != _colors.count) + { + println("colors array and labels array need to be of same size"); + + while (colors.count > labels.count) + { + self._colors.removeLast(); + } + while (labels.count > colors.count) + { + self._labels.removeLast(); + } + } + } + + public func getLabel(index: Int) -> String? + { + return _labels[index]; + } + + public func apply(legend: ChartLegend) + { + position = legend.position; + font = legend.font; + textColor = legend.textColor; + form = legend.form; + formSize = legend.formSize; + formLineWidth = legend.formLineWidth; + xEntrySpace = legend.xEntrySpace; + yEntrySpace = legend.yEntrySpace; + formToTextSpace = legend.formToTextSpace; + stackSpace = legend.stackSpace; + enabled = legend.enabled; + xOffset = legend.xOffset; + yOffset = legend.yOffset; + } + + public func getFullSize(labelFont: UIFont) -> CGSize + { + var width = CGFloat(0.0); + var height = CGFloat(0.0); + + for (var i = 0; i < _labels.count; i++) + { + if (labels[i] != nil) + { + // make a step to the left + if (_colors[i] != nil) + { + width += formSize + formToTextSpace; + } + + var size = (_labels[i] as NSString!).sizeWithAttributes([NSFontAttributeName: labelFont]); + + width += size.width; + width += xEntrySpace; + + height += size.height; + height += yEntrySpace; + } + else + { + width += formSize + stackSpace; + } + } + + return CGSize(width: width, height: height); + } + + public var neededWidth = CGFloat(0.0); + public var neededHeight = CGFloat(0.0); + public var textWidthMax = CGFloat(0.0); + public var textHeightMax = CGFloat(0.0); + + public func calculateDimensions(labelFont: UIFont) + { + var maxEntrySize = getMaximumEntrySize(labelFont); + var fullSize = getFullSize(labelFont); + + if (position == .RightOfChart + || position == .RightOfChartCenter + || position == .PiechartCenter) + { + neededWidth = maxEntrySize.width; + neededHeight = fullSize.height; + textWidthMax = maxEntrySize.width; + textHeightMax = maxEntrySize.height; + } + else + { + neededWidth = fullSize.width; + neededHeight = maxEntrySize.height; + textWidthMax = maxEntrySize.width; + textHeightMax = maxEntrySize.height; + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Components/ChartLimitLine.swift b/Charts/Classes/Components/ChartLimitLine.swift new file mode 100644 index 0000000000..1309f062ee --- /dev/null +++ b/Charts/Classes/Components/ChartLimitLine.swift @@ -0,0 +1,76 @@ +// +// ChartLimitLine.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit + +public class ChartLimitLine: ChartComponentBase +{ + @objc + public enum LabelPosition: Int + { + case Left + case Right + } + + public var limit = Float(0.0) + private var _lineWidth = CGFloat(2.0) + public var lineColor = UIColor(red: 237.0/255.0, green: 91.0/255.0, blue: 91.0/255.0, alpha: 1.0) + public var lineDashPhase = CGFloat(0.0) + public var lineDashLengths: [CGFloat]? + public var valueTextColor = UIColor.blackColor() + public var valueFont = UIFont.systemFontOfSize(13.0) + public var label = "" + public var labelPosition = LabelPosition.Right + + public override init() + { + super.init(); + } + + public init(limit: Float) + { + super.init(); + self.limit = limit; + } + + public init(limit: Float, label: String) + { + super.init(); + self.limit = limit; + self.label = label; + } + + /// set the line width of the chart (min = 0.2f, max = 12f); default 2f + public var lineWidth: CGFloat + { + get + { + return _lineWidth; + } + set + { + _lineWidth = newValue; + + if (_lineWidth < 0.2) + { + _lineWidth = 0.2; + } + if (_lineWidth > 12.0) + { + _lineWidth = 12.0; + } + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Components/ChartMarker.swift b/Charts/Classes/Components/ChartMarker.swift new file mode 100644 index 0000000000..8e38f946ac --- /dev/null +++ b/Charts/Classes/Components/ChartMarker.swift @@ -0,0 +1,57 @@ +// +// ChartMarker.swift +// Charts +// +// Created by Daniel Cohen Gindi on 3/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit; + +public class ChartMarker: ChartComponentBase +{ + /// The marker image to render + public var image: UIImage? + + /// Use this to return the desired offset you wish the MarkerView to have on the x-axis. + public var offset: CGPoint = CGPoint() + + /// The marker's size + public var size: CGSize + { + get + { + return image!.size; + } + } + + public override init() + { + super.init(); + } + + /// Draws the ChartMarker on the given position on the given context + public func draw(#context: CGContext, point: CGPoint) + { + var offset = self.offset; + var size = self.size; + + var rect = CGRect(x: point.x + offset.x, y: point.y + offset.y, width: size.width, height: size.height); + + UIGraphicsPushContext(context); + image!.drawInRect(rect); + UIGraphicsPopContext(); + } + + /// This method enables a custom ChartMarker to update it's content everytime the MarkerView is redrawn according to the data entry it points to. + public func refreshContent(#entry: ChartDataEntry, dataSetIndex: Int) + { + // Do nothing here... + } +} \ No newline at end of file diff --git a/Charts/Classes/Components/ChartXAxis.swift b/Charts/Classes/Components/ChartXAxis.swift new file mode 100644 index 0000000000..ac20743f64 --- /dev/null +++ b/Charts/Classes/Components/ChartXAxis.swift @@ -0,0 +1,83 @@ +// +// ChartXAxis.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartXAxis: ChartAxisBase +{ + @objc + public enum XAxisLabelPosition: Int + { + case Top + case Bottom + case BothSided + case TopInside + case BottomInside + } + + public var values = [String]() + public var labelWidth = CGFloat(1.0) + public var labelHeight = CGFloat(1.0) + + /// the space that should be left out (in characters) between the x-axis labels + public var spaceBetweenLabels = Int(4) + + /// the modulus that indicates if a value at a specified index in an array(list) for the x-axis-labels is drawn or not. Draw when (index % modulus) == 0. + public var axisLabelModulus = Int(1) + + /// the modulus that indicates if a value at a specified index in an array(list) for the y-axis-labels is drawn or not. Draw when (index % modulus) == 0. + /// Used only for Horizontal BarChart + public var yAxisLabelModulus = Int(1) + + /// if set to true, the chart will avoid that the first and last label entry in the chart "clip" off the edge of the chart + public var avoidFirstLastClippingEnabled = false + + /// if set to true, the x-axis label entries will adjust themselves when scaling the graph + public var adjustXLabelsEnabled = true + + /// the position of the x-labels relative to the chart + public var labelPosition = XAxisLabelPosition.Top; + + public override init() + { + super.init(); + } + + public override func getLongestLabel() -> String + { + var longest = ""; + + for (var i = 0; i < values.count; i++) + { + var text = values[i]; + + if (longest.lengthOfBytesUsingEncoding(NSUTF16StringEncoding) < text.lengthOfBytesUsingEncoding(NSUTF16StringEncoding)) + { + longest = text; + } + } + + return longest; + } + + public var isAvoidFirstLastClippingEnabled: Bool + { + return avoidFirstLastClippingEnabled; + } + + public var isAdjustXLabelsEnabled: Bool + { + return adjustXLabelsEnabled; + } +} \ No newline at end of file diff --git a/Charts/Classes/Components/ChartYAxis.swift b/Charts/Classes/Components/ChartYAxis.swift new file mode 100644 index 0000000000..da8cc504dc --- /dev/null +++ b/Charts/Classes/Components/ChartYAxis.swift @@ -0,0 +1,246 @@ +// +// ChartYAxis.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit + +/// Class representing the y-axis labels settings and its entries. +/// Be aware that not all features the YLabels class provides are suitable for the RadarChart. +/// Customizations that affect the value range of the axis need to be applied before setting data for the chart. +public class ChartYAxis: ChartAxisBase +{ + @objc + public enum YAxisLabelPosition: Int + { + case OutsideChart + case InsideChart + } + + /// Enum that specifies the axis a DataSet should be plotted against, either Left or Right. + @objc + public enum AxisDependency: Int + { + case Left + case Right + } + + public var entries = [Float]() + public var entryCount: Int { return entries.count; } + + /// the number of y-label entries the y-labels should have, default 6 + private var _labelCount = Int(6) + + /// indicates if the top y-label entry is drawn or not + public var drawTopYLabelEntryEnabled = true + + /// if true, the y-labels show only the minimum and maximum value + public var showOnlyMinMaxEnabled = false + + /// flag that indicates if the axis is inverted or not + public var inverted = false + + /// if true, the y-label entries will always start at zero + public var startAtZeroEnabled = true + + /// the formatter used to customly format the y-labels + public var valueFormatter: NSNumberFormatter? + + /// the formatter used to customly format the y-labels + internal var _defaultValueFormatter = NSNumberFormatter() + + /// array of limitlines that can be set for the axis + private var _limitLines = [ChartLimitLine]() + + /// A custom minimum value for this axis. + /// If set, this value will not be calculated automatically depending on the provided data. + /// Use resetcustomAxisMin() to undo this. + /// Do not forget to set startAtZeroEnabled = false if you use this method. + /// Otherwise, the axis-minimum value will still be forced to 0. + public var customAxisMin = Float.NaN + + /// Set a custom maximum value for this axis. + /// If set, this value will not be calculated automatically depending on the provided data. + /// Use resetcustomAxisMax() to undo this. + public var customAxisMax = Float.NaN + + /// axis space from the largest value to the top in percent of the total axis range + public var spaceTop = CGFloat(0.1) + + /// axis space from the smallest value to the bottom in percent of the total axis range + public var spaceBottom = CGFloat(0.1) + + public var axisMaximum = Float(0) + public var axisMinimum = Float(0) + + /// the total range of values this axis covers + public var axisRange = Float(0) + + /// the position of the y-labels relative to the chart + public var labelPosition = YAxisLabelPosition.OutsideChart + + /// the side this axis object represents + private var _axisDependency = AxisDependency.Left + + public override init() + { + super.init(); + + _defaultValueFormatter.maximumFractionDigits = 1; + _defaultValueFormatter.minimumFractionDigits = 1; + _defaultValueFormatter.usesGroupingSeparator = true; + } + + public init(position: AxisDependency) + { + super.init(); + + _axisDependency = position; + + _defaultValueFormatter.maximumFractionDigits = 1; + _defaultValueFormatter.minimumFractionDigits = 1; + _defaultValueFormatter.usesGroupingSeparator = true; + } + + public var axisDependency: AxisDependency + { + return _axisDependency; + } + + /// the number of label entries the y-axis should have + /// max = 15, + /// min = 2, + /// default = 6, + /// be aware that this number is not fixed and can only be approximated + public var labelCount: Int + { + get + { + return _labelCount; + } + set + { + _labelCount = newValue; + + if (_labelCount > 15) + { + _labelCount = 15; + } + if (_labelCount < 2) + { + _labelCount = 2; + } + } + } + + /// Adds a new ChartLimitLine to this axis. + public func addLimitLine(line: ChartLimitLine) + { + _limitLines.append(line); + } + + /// Removes the specified ChartLimitLine from the axis. + public func removeLimitLine(line: ChartLimitLine) + { + for (var i = 0; i < _limitLines.count; i++) + { + if (_limitLines[i] === line) + { + _limitLines.removeAtIndex(i); + return; + } + } + } + + /// Removes all LimitLines from the axis. + public func removeAllLimitLines() + { + _limitLines.removeAll(keepCapacity: false); + } + + /// Returns the LimitLines of this axis. + public var limitLines : [ChartLimitLine] + { + return _limitLines; + } + + /// By calling this method, any custom minimum value that has been previously set is reseted, and the calculation is done automatically. + public func resetcustomAxisMin() + { + customAxisMin = Float.NaN; + } + + /// By calling this method, any custom maximum value that has been previously set is reseted, and the calculation is done automatically. + public func resetcustomAxisMax() + { + customAxisMax = Float.NaN; + } + + public func requiredSize() -> CGSize + { + var label = getLongestLabel() as NSString; + var size = label.sizeWithAttributes([NSFontAttributeName: labelFont]); + size.width += xOffset * 2.0; + size.height += yOffset * 2.0; + return size; + } + + public override func getLongestLabel() -> String + { + var longest = ""; + + for (var i = 0; i < entries.count; i++) + { + var text = getFormattedLabel(i); + + if (longest.lengthOfBytesUsingEncoding(NSUTF16StringEncoding) < text.lengthOfBytesUsingEncoding(NSUTF16StringEncoding)) + { + longest = text; + } + } + + return longest; + } + + /// Returns the formatted y-label at the specified index. This will either use the auto-formatter or the custom formatter (if one is set). + public func getFormattedLabel(index: Int) -> String + { + if (index < 0 || index >= entries.count) + { + return ""; + } + + return (valueFormatter ?? _defaultValueFormatter).stringFromNumber(entries[index])!; + } + + /// Returns true if this axis needs horizontal offset, false if no offset is needed. + public var needsOffset: Bool + { + if (isEnabled && isDrawLabelsEnabled && labelPosition == .OutsideChart) + { + return true; + } + else + { + return false; + } + } + + public var isInverted: Bool { return inverted; } + + public var isStartAtZeroEnabled: Bool { return startAtZeroEnabled; } + + public var isShowOnlyMinMaxEnabled: Bool { return showOnlyMinMaxEnabled; } + + public var isDrawTopYLabelEntryEnabled: Bool { return drawTopYLabelEntryEnabled; } +} \ No newline at end of file diff --git a/Charts/Classes/Data/BarChartData.swift b/Charts/Classes/Data/BarChartData.swift new file mode 100644 index 0000000000..f5c854eb6e --- /dev/null +++ b/Charts/Classes/Data/BarChartData.swift @@ -0,0 +1,42 @@ +// +// BarChartData.swift +// Charts +// +// Created by Daniel Cohen Gindi on 26/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class BarChartData: BarLineScatterCandleChartData +{ + private var _groupSpace = CGFloat(0.8) + + /// The spacing is relative to a full bar width + public var groupSpace: CGFloat + { + get + { + if (_dataSets.count <= 1) + { + return 0.0; + } + return _groupSpace; + } + set + { + _groupSpace = newValue; + } + } + + /// Returns true if this BarData object contains grouped DataSets (more than 1 DataSet). + public var isGrouped: Bool + { + return _dataSets.count > 1 ? true : false; + } +} \ No newline at end of file diff --git a/Charts/Classes/Data/BarChartDataEntry.swift b/Charts/Classes/Data/BarChartDataEntry.swift new file mode 100644 index 0000000000..c0bc6ecb83 --- /dev/null +++ b/Charts/Classes/Data/BarChartDataEntry.swift @@ -0,0 +1,109 @@ +// +// BarChartDataEntry.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class BarChartDataEntry: ChartDataEntry +{ + /// the values the stacked barchart holds + public var values: [Float]! + + /// Constructor for stacked bar entries. + public init(values: [Float], xIndex: Int) + { + super.init(value: BarChartDataEntry.calcSum(values), xIndex: xIndex); + self.values = values; + } + + /// Constructor for normal bars (not stacked). + public override init(value: Float, xIndex: Int) + { + super.init(value: value, xIndex: xIndex); + } + + /// Constructor for stacked bar entries. + public init(values: [Float], xIndex: Int, label: String) + { + super.init(value: BarChartDataEntry.calcSum(values), xIndex: xIndex, data: label); + self.values = values; + } + + /// Constructor for normal bars (not stacked). + public override init(value: Float, xIndex: Int, data: AnyObject?) + { + super.init(value: value, xIndex: xIndex, data: data) + } + + /// Returns the closest value inside the values array (for stacked barchart) + /// to the value given as a parameter. The closest value must be higher + /// (above) the provided value. + public func getClosestIndexAbove(value: Float) -> Int + { + if (values == nil) + { + return 0; + } + + var index = values.count - 1; + var remainder: Float = 0.0; + + while (index > 0 && value > values[index] + remainder) + { + remainder += values[index]; + index--; + } + + return index; + } + + public func getBelowSum(stackIndex :Int) -> Float + { + if (values == nil) + { + return 0; + } + + var remainder: Float = 0.0; + var index = values.count - 1; + + while (index > stackIndex && index >= 0) + { + remainder += values[index]; + index--; + } + + return remainder; + } + + /// Calculates the sum across all values. + private class func calcSum(values: [Float]) -> Float + { + var sum = Float(0.0); + + for f in values + { + sum += f; + } + + return sum; + } + + // MARK: NSCopying + + public override func copyWithZone(zone: NSZone) -> AnyObject + { + var copy = super.copyWithZone(zone) as! BarChartDataEntry; + copy.values = values; + return copy; + } +} \ No newline at end of file diff --git a/Charts/Classes/Data/BarChartDataSet.swift b/Charts/Classes/Data/BarChartDataSet.swift new file mode 100644 index 0000000000..002413e9dd --- /dev/null +++ b/Charts/Classes/Data/BarChartDataSet.swift @@ -0,0 +1,113 @@ +// +// BarChartDataSet.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class BarChartDataSet: BarLineScatterCandleChartDataSet +{ + /// space indicator between the bars in percentage of the whole width of one value (0.15 == 15% of bar width) + public var barSpace: CGFloat = 0.15 + + /// the maximum number of bars that are stacked upon each other, this value + /// is calculated from the Entries that are added to the DataSet + private var _stackSize = 1 + + /// the color used for drawing the bar-shadows. The bar shadows is a surface behind the bar that indicates the maximum value + public var barShadowColor = UIColor(red: 215.0/255.0, green: 215.0/255.0, blue: 215.0/255.0, alpha: 1.0) + + /// the alpha value (transparency) that is used for drawing the highlight indicator bar. min = 0.0 (fully transparent), max = 1.0 (fully opaque) + public var highLightAlpha = CGFloat(120.0 / 255.0) + + /// the overall entry count, including counting each stack-value individually + private var _entryCountStacks = 0 + + /// array of labels used to describe the different values of the stacked bars + public var stackLabels: [String] = ["Stack"] + + public override init(yVals: [ChartDataEntry]?, label: String) + { + super.init(yVals: yVals, label: label); + + self.highlightColor = UIColor.blackColor(); + + self.calcStackSize(yVals as! [BarChartDataEntry]?); + self.calcEntryCountIncludingStacks(yVals as! [BarChartDataEntry]?); + } + + // MARK: NSCopying + + public override func copyWithZone(zone: NSZone) -> AnyObject + { + var copy = super.copyWithZone(zone) as! BarChartDataSet; + copy.barSpace = barSpace; + copy._stackSize = _stackSize; + copy.barShadowColor = barShadowColor; + copy.highLightAlpha = highLightAlpha; + copy._entryCountStacks = _entryCountStacks; + copy.stackLabels = stackLabels; + return copy; + } + + /// Calculates the total number of entries this DataSet represents, including + /// stacks. All values belonging to a stack are calculated separately. + private func calcEntryCountIncludingStacks(yVals: [BarChartDataEntry]!) + { + _entryCountStacks = 0; + + for (var i = 0; i < yVals.count; i++) + { + var vals = yVals[i].values; + + if (vals == nil) + { + _entryCountStacks++; + } + else + { + _entryCountStacks += vals.count; + } + } + } + + /// calculates the maximum stacksize that occurs in the Entries array of this DataSet + private func calcStackSize(yVals: [BarChartDataEntry]!) + { + for (var i = 0; i < yVals.count; i++) + { + var vals = yVals[i].values; + + if (vals != nil && vals.count > _stackSize) + { + _stackSize = vals.count; + } + } + } + + /// Returns the maximum number of bars that can be stacked upon another in this DataSet. + public var stackSize: Int + { + return _stackSize; + } + + /// Returns true if this DataSet is stacked (stacksize > 1) or not. + public var isStacked: Bool + { + return _stackSize > 1 ? true : false; + } + + /// returns the overall entry count, including counting each stack-value individually + public var entryCountStacks: Int + { + return _entryCountStacks; + } +} \ No newline at end of file diff --git a/Charts/Classes/Data/BarLineScatterCandleChartData.swift b/Charts/Classes/Data/BarLineScatterCandleChartData.swift new file mode 100644 index 0000000000..d5ea9f0dbf --- /dev/null +++ b/Charts/Classes/Data/BarLineScatterCandleChartData.swift @@ -0,0 +1,32 @@ +// +// BarLineScatterCandleChartData.swift +// Charts +// +// Created by Daniel Cohen Gindi on 26/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class BarLineScatterCandleChartData: ChartData +{ + public override init() + { + super.init(); + } + + public override init(xVals: [String]?) + { + super.init(xVals: xVals); + } + + public override init(xVals: [String]?, dataSets: [ChartDataSet]?) + { + super.init(xVals: xVals, dataSets: dataSets); + } +} diff --git a/Charts/Classes/Data/BarLineScatterCandleChartDataSet.swift b/Charts/Classes/Data/BarLineScatterCandleChartDataSet.swift new file mode 100644 index 0000000000..d60f245052 --- /dev/null +++ b/Charts/Classes/Data/BarLineScatterCandleChartDataSet.swift @@ -0,0 +1,35 @@ +// +// BarLineScatterCandleChartDataSet.swift +// Charts +// +// Created by Daniel Cohen Gindi on 26/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit; + +public class BarLineScatterCandleChartDataSet: ChartDataSet +{ + public var highlightColor = UIColor(red: 255.0/255.0, green: 187.0/255.0, blue: 115.0/255.0, alpha: 1.0) + public var highlightLineWidth = CGFloat(1.0) + public var highlightLineDashPhase = CGFloat(0.0) + public var highlightLineDashLengths: [CGFloat]? + + // MARK: NSCopying + + public override func copyWithZone(zone: NSZone) -> AnyObject + { + var copy = super.copyWithZone(zone) as! BarLineScatterCandleChartDataSet; + copy.highlightColor = highlightColor; + copy.highlightLineWidth = highlightLineWidth; + copy.highlightLineDashPhase = highlightLineDashPhase; + copy.highlightLineDashLengths = highlightLineDashLengths; + return copy; + } +} diff --git a/Charts/Classes/Data/CandleChartData.swift b/Charts/Classes/Data/CandleChartData.swift new file mode 100644 index 0000000000..836e835b33 --- /dev/null +++ b/Charts/Classes/Data/CandleChartData.swift @@ -0,0 +1,19 @@ +// +// CandleChartData.swift +// Charts +// +// Created by Daniel Cohen Gindi on 26/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class CandleChartData: BarLineScatterCandleChartData +{ + +} diff --git a/Charts/Classes/Data/CandleChartDataEntry.swift b/Charts/Classes/Data/CandleChartDataEntry.swift new file mode 100644 index 0000000000..49b45ee548 --- /dev/null +++ b/Charts/Classes/Data/CandleChartDataEntry.swift @@ -0,0 +1,86 @@ +// +// CandleChartDataEntry.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class CandleChartDataEntry: ChartDataEntry +{ + /// shadow-high value + public var high = Float(0.0) + + /// shadow-low value + public var low = Float(0.0) + + /// close value + public var close = Float(0.0) + + /// open value + public var open = Float(0.0) + + public init(xIndex: Int, shadowH: Float, shadowL: Float, open: Float, close: Float) + { + super.init(value: (shadowH + shadowL) / 2.0, xIndex: xIndex); + + self.high = shadowH; + self.low = shadowL; + self.open = open; + self.close = close; + } + + public init(xIndex: Int, shadowH: Float, shadowL: Float, open: Float, close: Float, data: AnyObject?) + { + super.init(value: (shadowH + shadowL) / 2.0, xIndex: xIndex, data: data); + + self.high = shadowH; + self.low = shadowL; + self.open = open; + self.close = close; + } + + /// Returns the overall range (difference) between shadow-high and shadow-low. + public var shadowRange: Float + { + return abs(high - low); + } + + /// Returns the body size (difference between open and close). + public var bodyRange: Float + { + return abs(open - close); + } + + /// the center value of the candle. (Middle value between high and low) + public override var value: Float + { + get + { + return super.value; + } + set + { + super.value = (high + low) / 2.0; + } + } + + // MARK: NSCopying + + public override func copyWithZone(zone: NSZone) -> AnyObject + { + var copy = super.copyWithZone(zone) as! CandleChartDataEntry; + copy.high = high; + copy.high = low; + copy.high = open; + copy.high = close; + return copy; + } +} \ No newline at end of file diff --git a/Charts/Classes/Data/CandleChartDataSet.swift b/Charts/Classes/Data/CandleChartDataSet.swift new file mode 100644 index 0000000000..19f77fdfd5 --- /dev/null +++ b/Charts/Classes/Data/CandleChartDataSet.swift @@ -0,0 +1,81 @@ +// +// CandleChartDataSet.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class CandleChartDataSet: BarLineScatterCandleChartDataSet +{ + /// the width of the candle-shadow-line in pixels. + /// :default: 3.0 + public var shadowWidth = CGFloat(1.5) + + /// the space between the candle entries + /// :default: 0.1 (10%) + private var _bodySpace = CGFloat(0.1) + + public override init(yVals: [ChartDataEntry]?, label: String) + { + super.init(yVals: yVals, label: label); + } + + internal override func calcMinMax() + { + if (yVals.count == 0) + { + return; + } + + var entries = yVals as! [CandleChartDataEntry]; + + _yMin = entries[0].low; + _yMax = entries[0].high; + + for (var i = 0; i < entries.count; i++) + { + var e = entries[i]; + + if (e.low < _yMin) + { + _yMin = e.low; + } + + if (e.high > _yMax) + { + _yMax = e.high; + } + } + } + + /// the space that is left out on the left and right side of each candle, + /// :default: 0.1 (10%), max 0.45, min 0.0 + public var bodySpace: CGFloat + { + set + { + _bodySpace = newValue; + + if (_bodySpace < 0.0) + { + _bodySpace = 0.0; + } + if (_bodySpace > 0.45) + { + _bodySpace = 0.45; + } + } + get + { + return _bodySpace; + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Data/ChartData.swift b/Charts/Classes/Data/ChartData.swift new file mode 100644 index 0000000000..7ee79d6251 --- /dev/null +++ b/Charts/Classes/Data/ChartData.swift @@ -0,0 +1,831 @@ +// +// ChartData.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit + +public class ChartData: NSObject +{ + internal var _yMax = Float(0.0) + internal var _yMin = Float(0.0) + internal var _leftAxisMax = Float(0.0) + internal var _leftAxisMin = Float(0.0) + internal var _rightAxisMax = Float(0.0) + internal var _rightAxisMin = Float(0.0) + private var _yValueSum = Float(0.0) + private var _yValCount = Int(0) + + /// the average length (in characters) across all x-value strings + private var _xValAverageLength = Float(0.0) + + internal var _xVals: [String]! + internal var _dataSets: [ChartDataSet]! + + public override init() + { + super.init(); + + _xVals = [String](); + _dataSets = [ChartDataSet](); + } + + public init(xVals: [String]?) + { + super.init(); + + _xVals = xVals == nil ? [String]() : xVals; + _dataSets = [ChartDataSet](); + + self.initialize(_dataSets); + } + + public convenience init(xVals: [String]?, dataSet: ChartDataSet?) + { + self.init(xVals: xVals, dataSets: dataSet === nil ? nil : [dataSet!]); + } + + public init(xVals: [String]?, dataSets: [ChartDataSet]?) + { + super.init() + + _xVals = xVals == nil ? [String]() : xVals; + _dataSets = dataSets == nil ? [ChartDataSet]() : dataSets; + + self.initialize(_dataSets) + } + + internal func initialize(dataSets: [ChartDataSet]) + { + checkIsLegal(dataSets); + + calcMinMax(); + calcYValueSum(); + calcYValueCount(); + + calcXValAverageLength() + } + + // calculates the average length (in characters) across all x-value strings + internal func calcXValAverageLength() + { + if (_xVals.count == 0) + { + _xValAverageLength = 1; + return; + } + + var sum = 1; + + for (var i = 0; i < _xVals.count; i++) + { + sum += _xVals[i].lengthOfBytesUsingEncoding(NSUTF16StringEncoding); + } + + _xValAverageLength = Float(sum) / Float(_xVals.count); + } + + // Checks if the combination of x-values array and DataSet array is legal or not. + // :param: dataSets + internal func checkIsLegal(dataSets: [ChartDataSet]!) + { + if (dataSets == nil) + { + return; + } + + for (var i = 0; i < dataSets.count; i++) + { + if (dataSets[i].yVals.count > _xVals.count) + { + println("One or more of the DataSet Entry arrays are longer than the x-values array of this Data object."); + return; + } + } + } + + public func notifyDataChanged() + { + initialize(_dataSets); + } + + /// calc minimum and maximum y value over all datasets + internal func calcMinMax() + { + if (_dataSets == nil || _dataSets.count < 1) + { + _yMax = 0.0; + _yMin = 0.0; + } + else + { + // calculate absolute min and max + _yMin = _dataSets[0].yMin; + _yMax = _dataSets[0].yMax; + + for (var i = 0; i < _dataSets.count; i++) + { + if (_dataSets[i].yMin < _yMin) + { + _yMin = _dataSets[i].yMin; + } + + if (_dataSets[i].yMax > _yMax) + { + _yMax = _dataSets[i].yMax; + } + } + + // left axis + var firstLeft = getFirstLeft(); + + if (firstLeft !== nil) + { + _leftAxisMax = firstLeft!.yMax; + _leftAxisMin = firstLeft!.yMin; + + for dataSet in _dataSets + { + if (dataSet.axisDependency == .Left) + { + if (dataSet.yMin < _leftAxisMin) + { + _leftAxisMin = dataSet.yMin; + } + + if (dataSet.yMax > _leftAxisMax) + { + _leftAxisMax = dataSet.yMax; + } + } + } + } + + // right axis + var firstRight = getFirstRight(); + + if (firstRight !== nil) + { + _rightAxisMax = firstRight!.yMax; + _rightAxisMin = firstRight!.yMin; + + for dataSet in _dataSets + { + if (dataSet.axisDependency == .Right) + { + if (dataSet.yMin < _rightAxisMin) + { + _rightAxisMin = dataSet.yMin; + } + + if (dataSet.yMax > _rightAxisMax) + { + _rightAxisMax = dataSet.yMax; + } + } + } + } + + // in case there is only one axis, adjust the second axis + handleEmptyAxis(firstLeft, firstRight: firstRight); + } + } + + /// calculates the sum of all y-values in all datasets + internal func calcYValueSum() + { + _yValueSum = 0; + + if (_dataSets == nil) + { + return; + } + + for (var i = 0; i < _dataSets.count; i++) + { + _yValueSum += fabsf(_dataSets[i].yValueSum); + } + } + + /// Calculates the total number of y-values across all ChartDataSets the ChartData represents. + internal func calcYValueCount() + { + _yValCount = 0; + + if (_dataSets == nil) + { + return; + } + + var count = 0; + + for (var i = 0; i < _dataSets.count; i++) + { + count += _dataSets[i].entryCount; + } + + _yValCount = count; + } + + /// returns the number of LineDataSets this object contains + public var dataSetCount: Int + { + if (_dataSets == nil) + { + return 0; + } + return _dataSets.count; + } + + /// returns the smallest y-value the data object contains. + public var yMin: Float + { + return _yMin; + } + + public func getYMin() -> Float + { + return _yMin; + } + + public func getYMin(axis: ChartYAxis.AxisDependency) -> Float + { + if (axis == .Left) + { + return _leftAxisMin; + } + else + { + return _rightAxisMin; + } + } + + /// returns the greatest y-value the data object contains. + public var yMax: Float + { + return _yMax; + } + + public func getYMax() -> Float + { + return _yMax; + } + + public func getYMax(axis: ChartYAxis.AxisDependency) -> Float + { + if (axis == .Left) + { + return _leftAxisMax; + } + else + { + return _rightAxisMax; + } + } + + /// returns the average length (in characters) across all values in the x-vals array + public var xValAverageLength: Float + { + return _xValAverageLength; + } + + /// returns the total y-value sum across all DataSet objects the this object represents. + public var yValueSum: Float + { + return _yValueSum; + } + + /// Returns the total number of y-values across all DataSet objects the this object represents. + public var yValCount: Int + { + return _yValCount; + } + + /// returns the x-values the chart represents + public var xVals: [String] + { + return _xVals; + } + + ///Adds a new x-value to the chart data. + public func addXValue(xVal: String) + { + _xVals.append(xVal); + } + + /// Removes the x-value at the specified index. + public func removeXValue(index: Int) + { + _xVals.removeAtIndex(index); + } + + /// Returns the array of ChartDataSets this object holds. + public var dataSets: [ChartDataSet] + { + get + { + return _dataSets; + } + set + { + _dataSets = newValue; + } + } + + /// Retrieve the index of a ChartDataSet with a specific label from the ChartData. Search can be case sensitive or not. + /// IMPORTANT: This method does calculations at runtime, do not over-use in performance critical situations. + /// + /// :param: dataSets the DataSet array to search + /// :param: type + /// :param: ignorecase if true, the search is not case-sensitive + /// :returns: + internal func getDataSetIndexByLabel(label: String, ignorecase: Bool) -> Int + { + if (ignorecase) + { + for (var i = 0; i < dataSets.count; i++) + { + if (label.caseInsensitiveCompare(dataSets[i].label) == NSComparisonResult.OrderedSame) + { + return i; + } + } + } + else + { + for (var i = 0; i < dataSets.count; i++) + { + if (label == dataSets[i].label) + { + return i; + } + } + } + + return -1; + } + + /// returns the total number of x-values this ChartData object represents (the size of the x-values array) + public var xValCount: Int + { + return _xVals.count; + } + + /// Returns the labels of all DataSets as a string array. + internal func dataSetLabels() -> [String] + { + var types = [String](); + + for (var i = 0; i < _dataSets.count; i++) + { + types[i] = _dataSets[i].label; + } + + return types; + } + + /// Get the Entry for a corresponding highlight object + /// + /// :param: highlight + /// :returns: the entry that is highlighted + public func getEntryForHighlight(highlight: ChartHighlight) -> ChartDataEntry + { + return _dataSets[highlight.dataSetIndex].entryForXIndex(highlight.xIndex); + } + + /// Returns the DataSet object with the given label. + /// sensitive or not. + /// IMPORTANT: This method does calculations at runtime. Use with care in performance critical situations. + /// + /// :param: label + /// :param: ignorecase + public func getDataSetByLabel(label: String, ignorecase: Bool) -> ChartDataSet? + { + var index = getDataSetIndexByLabel(label, ignorecase: ignorecase); + + if (index < 0 || index >= _dataSets.count) + { + return nil; + } + else + { + return _dataSets[index]; + } + } + + public func getDataSetByIndex(index: Int) -> ChartDataSet! + { + if (_dataSets == nil || index < 0 || index >= _dataSets.count) + { + return nil; + } + + return _dataSets[index]; + } + + public func addDataSet(d: ChartDataSet!) + { + if (_dataSets == nil) + { + return; + } + + _yValCount += d.entryCount; + _yValueSum += d.yValueSum; + + if (_dataSets.count == 0) + { + _yMax = d.yMax; + _yMin = d.yMin; + + if (d.axisDependency == .Left) + { + _leftAxisMax = d.yMax; + _leftAxisMin = d.yMin; + } + else + { + _rightAxisMax = d.yMax; + _rightAxisMin = d.yMin; + } + } + else + { + if (_yMax < d.yMax) + { + _yMax = d.yMax; + } + if (_yMin > d.yMin) + { + _yMin = d.yMin; + } + + if (d.axisDependency == .Left) + { + if (_leftAxisMax < d.yMax) + { + _leftAxisMax = d.yMax; + } + if (_leftAxisMin > d.yMin) + { + _leftAxisMin = d.yMin; + } + } + else + { + if (_rightAxisMax < d.yMax) + { + _rightAxisMax = d.yMax; + } + if (_rightAxisMin > d.yMin) + { + _rightAxisMin = d.yMin; + } + } + } + + _dataSets.append(d); + + handleEmptyAxis(getFirstLeft(), firstRight: getFirstRight()); + } + + public func handleEmptyAxis(firstLeft: ChartDataSet?, firstRight: ChartDataSet?) + { + // in case there is only one axis, adjust the second axis + if (firstLeft === nil) + { + _leftAxisMax = _rightAxisMax; + _leftAxisMin = _rightAxisMin; + } + else if (firstRight === nil) + { + _rightAxisMax = _leftAxisMax; + _rightAxisMin = _leftAxisMin; + } + } + + /// Removes the given DataSet from this data object. + /// Also recalculates all minimum and maximum values. + /// + /// :returns: true if a DataSet was removed, false if no DataSet could be removed. + public func removeDataSet(dataSet: ChartDataSet!) -> Bool + { + if (_dataSets == nil || dataSet === nil) + { + return false; + } + + var removed = false; + for (var i = 0; i < _dataSets.count; i++) + { + if (_dataSets[i] === dataSet) + { + return removeDataSetByIndex(i); + } + } + + return false; + } + + /// Removes the DataSet at the given index in the DataSet array from the data object. + /// Also recalculates all minimum and maximum values. + /// + /// :returns: true if a DataSet was removed, false if no DataSet could be removed. + public func removeDataSetByIndex(index: Int) -> Bool + { + if (_dataSets == nil || index >= _dataSets.count || index < 0) + { + return false; + } + + var d = _dataSets.removeAtIndex(index); + _yValCount -= d.entryCount; + _yValueSum -= d.yValueSum; + + calcMinMax(); + + return true; + } + + /// Adds an Entry to the DataSet at the specified index. Entries are added to the end of the list. + public func addEntry(e: ChartDataEntry, dataSetIndex: Int) + { + if (_dataSets != nil && _dataSets.count > dataSetIndex && dataSetIndex >= 0) + { + var val = e.value; + + _yValCount += 1; + _yValueSum += val; + + if (_yMax < val) + { + _yMax = val; + } + if (_yMin > val) + { + _yMin = val; + } + + var set = _dataSets[dataSetIndex]; + if (set.axisDependency == .Left) + { + if (_leftAxisMax < e.value) + { + _leftAxisMax = e.value; + } + if (_leftAxisMin > e.value) + { + _leftAxisMin = e.value; + } + } + else + { + if (_rightAxisMax < e.value) + { + _rightAxisMax = e.value; + } + if (_rightAxisMin > e.value) + { + _rightAxisMin = e.value; + } + } + + handleEmptyAxis(getFirstLeft(), firstRight: getFirstRight()); + + set.addEntry(e); + } + else + { + println("ChartData.addEntry() - dataSetIndex our of range."); + } + } + + /// Removes the given Entry object from the DataSet at the specified index. + public func removeEntry(entry: ChartDataEntry!, dataSetIndex: Int) -> Bool + { + // entry null, outofbounds + if (entry === nil || dataSetIndex >= _dataSets.count) + { + return false; + } + + // remove the entry from the dataset + var removed = _dataSets[dataSetIndex].removeEntry(xIndex: entry.xIndex); + + if (removed) + { + var val = entry.value; + + _yValCount -= 1; + _yValueSum -= val; + + calcMinMax(); + } + + return removed; + } + + /// Removes the Entry object at the given xIndex from the ChartDataSet at the + /// specified index. Returns true if an entry was removed, false if no Entry + /// was found that meets the specified requirements. + public func removeEntryByXIndex(xIndex: Int, dataSetIndex: Int) -> Bool + { + if (dataSetIndex >= _dataSets.count) + { + return false; + } + + var entry = _dataSets[dataSetIndex].entryForXIndex(xIndex); + + return removeEntry(entry, dataSetIndex: dataSetIndex); + } + + /// Returns the DataSet that contains the provided Entry, or null, if no DataSet contains this entry. + public func getDataSetForEntry(e: ChartDataEntry!) -> ChartDataSet? + { + if (e == nil) + { + return nil; + } + + for (var i = 0; i < _dataSets.count; i++) + { + var set = _dataSets[i]; + + for (var j = 0; j < set.entryCount; j++) + { + if (e === set.entryForXIndex(e.xIndex)) + { + return set; + } + } + } + + return nil; + } + + /// Returns the index of the provided DataSet inside the DataSets array of + /// this data object. Returns -1 if the DataSet was not found. + public func indexOfDataSet(dataSet: ChartDataSet) -> Int + { + for (var i = 0; i < _dataSets.count; i++) + { + if (_dataSets[i] === dataSet) + { + return i; + } + } + + return -1; + } + + public func getFirstLeft() -> ChartDataSet? + { + for dataSet in _dataSets + { + if (dataSet.axisDependency == .Left) + { + return dataSet; + } + } + + return nil; + } + + public func getFirstRight() -> ChartDataSet? + { + for dataSet in _dataSets + { + if (dataSet.axisDependency == .Right) + { + return dataSet; + } + } + + return nil; + } + + /// Returns all colors used across all DataSet objects this object represents. + public func getColors() -> [UIColor]? + { + if (_dataSets == nil) + { + return nil; + } + + var clrcnt = 0; + + for (var i = 0; i < _dataSets.count; i++) + { + clrcnt += _dataSets[i].colors.count; + } + + var colors = [UIColor](); + + for (var i = 0; i < _dataSets.count; i++) + { + var clrs = _dataSets[i].colors; + + for clr in clrs + { + colors.append(clr); + } + } + + return colors; + } + + /// Generates an x-values array filled with numbers in range specified by the parameters. Can be used for convenience. + public func generateXVals(from: Int, to: Int) -> [String] + { + var xvals = [String](); + + for (var i = from; i < to; i++) + { + xvals.append(String(i)); + } + + return xvals; + } + + /// Sets a custom ValueFormatter for all DataSets this data object contains. + public func setValueFormatter(formatter: NSNumberFormatter!) + { + for set in dataSets + { + set.valueFormatter = formatter; + } + } + + /// Sets the color of the value-text (color in which the value-labels are drawn) for all DataSets this data object contains. + public func setValueTextColor(color: UIColor!) + { + for set in dataSets + { + set.valueTextColor = color ?? set.valueTextColor; + } + } + + /// Sets the font for all value-labels for all DataSets this data object contains. + public func setValueFont(font: UIFont!) + { + for set in dataSets + { + set.valueFont = font ?? set.valueFont; + } + } + + /// Enables / disables drawing values (value-text) for all DataSets this data object contains. + public func setDrawValues(enabled: Bool) + { + for set in dataSets + { + set.drawValuesEnabled = enabled; + } + } + + /// Clears this data object from all DataSets and removes all Entries. + public func clearValues() + { + dataSets.removeAll(keepCapacity: false); + notifyDataChanged(); + } + + /// Checks if this data object contains the specified Entry. Returns true if so, false if not. + public func contains(#entry: ChartDataEntry) -> Bool + { + for set in dataSets + { + if (set.contains(entry)) + { + return true; + } + } + + return false; + } + + /// Checks if this data object contains the specified DataSet. Returns true if so, false if not. + public func contains(#dataSet: ChartDataSet) -> Bool + { + for set in dataSets + { + if (set.isEqual(dataSet)) + { + return true; + } + } + + return false; + } +} diff --git a/Charts/Classes/Data/ChartDataEntry.swift b/Charts/Classes/Data/ChartDataEntry.swift new file mode 100644 index 0000000000..e4913d5765 --- /dev/null +++ b/Charts/Classes/Data/ChartDataEntry.swift @@ -0,0 +1,129 @@ +// +// ChartDataEntry.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartDataEntry: NSObject, Equatable +{ + /// the actual value (y axis) + public var value = Float(0.0) + + /// the index on the x-axis + public var xIndex = Int(0) + + /// optional spot for additional data this Entry represents + public var data: AnyObject? + + public override init() + { + super.init(); + } + + public init(value: Float, xIndex: Int) + { + super.init(); + + self.value = value; + self.xIndex = xIndex; + } + + public init(value: Float, xIndex: Int, data: AnyObject?) + { + super.init(); + + self.value = value; + self.xIndex = xIndex; + self.data = data; + } + + // MARK: NSObject + + public override func isEqual(object: AnyObject?) -> Bool + { + if (object == nil) + { + return false; + } + + if (!object!.isKindOfClass(self.dynamicType)) + { + return false; + } + + if (object!.data !== data && !object!.isEqual(self)) + { + return false; + } + + if (object!.xIndex != xIndex) + { + return false; + } + + if (fabsf(object!.value - value) > 0.00001) + { + return false; + } + + return true; + } + + // MARK: NSObject + + public override var description: String + { + return "ChartDataEntry, xIndex: \(xIndex), value \(value)"; + } + + // MARK: NSCopying + + public func copyWithZone(zone: NSZone) -> AnyObject + { + var copy = self.dynamicType.allocWithZone(zone) as ChartDataEntry; + copy.value = value; + copy.xIndex = xIndex; + copy.data = data; + return copy; + } +} + +public func ==(lhs: ChartDataEntry, rhs: ChartDataEntry) -> Bool +{ + if (lhs === rhs) + { + return true; + } + + if (!lhs.isKindOfClass(rhs.dynamicType)) + { + return false; + } + + if (lhs.data !== rhs.data && !lhs.data!.isEqual(rhs.data)) + { + return false; + } + + if (lhs.xIndex != rhs.xIndex) + { + return false; + } + + if (fabsf(lhs.value - rhs.value) > 0.00001) + { + return false; + } + + return true; +} \ No newline at end of file diff --git a/Charts/Classes/Data/ChartDataSet.swift b/Charts/Classes/Data/ChartDataSet.swift new file mode 100644 index 0000000000..568bd73039 --- /dev/null +++ b/Charts/Classes/Data/ChartDataSet.swift @@ -0,0 +1,390 @@ +// +// ChartDataSet.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit + +public class ChartDataSet: NSObject +{ + public var colors = [UIColor]() + internal var _yVals: [ChartDataEntry]! + internal var _yMax = Float(0.0) + internal var _yMin = Float(0.0) + internal var _yValueSum = Float(0.0) + internal var _label = "DataSet" + public var visible = true; + public var drawValuesEnabled = true; + + /// the color used for the value-text + public var valueTextColor: UIColor = UIColor.blackColor() + + /// the font for the value-text labels + public var valueFont: UIFont = UIFont.systemFontOfSize(7.0) + + /// the formatter used to customly format the values + public var valueFormatter: NSNumberFormatter? + + /// the axis this DataSet should be plotted against. + public var axisDependency = ChartYAxis.AxisDependency.Left + + public var yVals: [ChartDataEntry] { return _yVals } + public var yValueSum: Float { return _yValueSum } + public var yMin: Float { return _yMin } + public var yMax: Float { return _yMax } + public var label: String { return _label } + + public override init() + { + super.init(); + } + + public init(yVals: [ChartDataEntry]?, label: String) + { + super.init(); + + _label = label; + _yVals = yVals == nil ? [ChartDataEntry]() : yVals; + + // default color + colors.append(UIColor(red: 140.0/255.0, green: 234.0/255.0, blue: 255.0/255.0, alpha: 1.0)); + + self.calcMinMax(); + self.calcYValueSum(); + } + + public convenience init(yVals: [ChartDataEntry]?) + { + self.init(yVals: yVals, label: "DataSet") + } + + internal func calcMinMax() + { + if _yVals!.count == 0 + { + return; + } + + _yMin = yVals[0].value; + _yMax = yVals[0].value; + + for var i = 0; i < _yVals.count; i++ + { + let e = _yVals[i]; + if (e.value < _yMin) + { + _yMin = e.value; + } + if (e.value > _yMax) + { + _yMax = e.value; + } + } + } + + private func calcYValueSum() + { + _yValueSum = 0; + + for var i = 0; i < _yVals.count; i++ + { + _yValueSum += fabsf(_yVals[i].value); + } + } + + public var entryCount: Int { return _yVals!.count; } + + public func yValForXIndex(x: Int) -> Float + { + let e = self.entryForXIndex(x); + + if (e !== nil) { return e.value } + else { return Float.NaN } + } + + /// Returns the first Entry object found at the given xIndex with binary search. + /// If the no Entry at the specifed x-index is found, this method returns the Entry at the closest x-index. + /// Returns nil if no Entry object at that index. + public func entryForXIndex(x: Int) -> ChartDataEntry! + { + var index = self.entryIndex(xIndex: x); + if (index > -1) + { + return _yVals[index]; + } + return nil; + } + + public func entriesForXIndex(x: Int) -> [ChartDataEntry] + { + var entries = [ChartDataEntry](); + + var low = 0; + var high = _yVals.count - 1; + + while (low <= high) + { + var m = Int((high + low) / 2); + var entry = _yVals[m]; + + if (x == entry.xIndex) + { + while (m > 0 && _yVals[m - 1].xIndex == x) + { + m--; + } + + high = _yVals.count; + for (; m < high; m++) + { + entry = _yVals[m]; + if (entry.xIndex == x) + { + entries.append(entry); + } + else + { + break; + } + } + } + + if (x > _yVals[m].xIndex) + { + low = m + 1; + } + else + { + high = m - 1; + } + } + + return entries; + } + + public func entryIndex(xIndex x: Int) -> Int + { + var low = 0; + var high = _yVals.count - 1; + var closest = -1; + + while (low <= high) + { + var m = (high + low) / 2; + var entry = _yVals[m]; + + if (x == entry.xIndex) + { + while (m > 0 && _yVals[m - 1].xIndex == x) + { + m--; + } + + return m; + } + + if (x > entry.xIndex) + { + low = m + 1; + } + else + { + high = m - 1; + } + + closest = m; + } + + return closest; + } + + public func entryIndex(entry e: ChartDataEntry, isEqual: Bool) -> Int + { + if (isEqual) + { + for (var i = 0; i < _yVals.count; i++) + { + if (_yVals[i].isEqual(e)) + { + return i; + } + } + } + else + { + for (var i = 0; i < _yVals.count; i++) + { + if (_yVals[i] === e) + { + return i; + } + } + } + + return -1 + } + + /// Returns the number of entries this DataSet holds. + public var valueCount: Int { return _yVals.count; } + + public func addEntry(e: ChartDataEntry) + { + var val = e.value; + + if (_yVals == nil || _yVals.count <= 0) + { + _yVals = [ChartDataEntry](); + _yMax = val; + _yMin = val; + } + else + { + if (_yMax < val) + { + _yMax = val; + } + if (_yMin > val) + { + _yMin = val; + } + } + + _yValueSum += val; + + _yVals.append(e); + } + + public func removeEntry(entry: ChartDataEntry) -> Bool + { + var removed = false; + + for (var i = 0; i < _yVals.count; i++) + { + if (_yVals[i] === entry) + { + _yVals.removeAtIndex(i); + removed = true; + break; + } + } + + if (removed) + { + _yValueSum -= entry.value; + calcMinMax(); + } + + return removed; + } + + public func removeEntry(#xIndex: Int) -> Bool + { + var index = self.entryIndex(xIndex: xIndex); + if (index > -1) + { + var e = _yVals.removeAtIndex(index); + + _yValueSum -= e.value; + calcMinMax(); + + return true; + } + + return false; + } + + public func resetColors() + { + colors.removeAll(keepCapacity: false); + } + + public func addColor(color: UIColor) + { + colors.append(color); + } + + public func setColor(color: UIColor) + { + colors.removeAll(keepCapacity: false); + colors.append(color); + } + + public func colorAt(var index: Int) -> UIColor + { + if (index < 0) + { + index = 0; + } + return colors[index % colors.count]; + } + + public var isVisible: Bool + { + return visible; + } + + public var isDrawValuesEnabled: Bool + { + return drawValuesEnabled; + } + + /// Checks if this DataSet contains the specified Entry. + /// :returns: true if contains the entry, false if not. + public func contains(e: ChartDataEntry) -> Bool + { + for entry in _yVals + { + if (entry.isEqual(e)) + { + return true; + } + } + + return false; + } + + // MARK: NSObject + + public override var description: String + { + return String(format: "ChartDataSet, label: %@, %i entries", arguments: [_label, _yVals.count]); + } + + public override var debugDescription: String + { + var desc = description + ":"; + + for (var i = 0; i < _yVals.count; i++) + { + desc += "\n" + _yVals[i].description; + } + + return desc; + } + + // MARK: NSCopying + + public func copyWithZone(zone: NSZone) -> AnyObject + { + var copy = self.dynamicType.allocWithZone(zone) as ChartDataSet; + copy.colors = colors; + copy._yVals = _yVals; + copy._yMax = _yMax; + copy._yMin = _yMin; + copy._yValueSum = _yValueSum; + copy._label = _label; + return copy; + } +} + + diff --git a/Charts/Classes/Data/CombinedChartData.swift b/Charts/Classes/Data/CombinedChartData.swift new file mode 100644 index 0000000000..1065bbe3e6 --- /dev/null +++ b/Charts/Classes/Data/CombinedChartData.swift @@ -0,0 +1,136 @@ +// +// CombinedChartData.swift +// Charts +// +// Created by Daniel Cohen Gindi on 26/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class CombinedChartData: BarLineScatterCandleChartData +{ + private var _lineData: LineChartData! + private var _barData: BarChartData! + private var _scatterData: ScatterChartData! + private var _candleData: CandleChartData! + + public override init() + { + super.init(); + } + + public override init(xVals: [String]?) + { + super.init(xVals: xVals); + } + + public var lineData: LineChartData! + { + get + { + return _lineData; + } + set + { + _lineData = newValue; + for dataSet in newValue.dataSets + { + _dataSets.append(dataSet); + } + + checkIsLegal(newValue.dataSets); + + calcMinMax(); + calcYValueSum(); + calcYValueCount(); + + calcXValAverageLength(); + } + } + + public var barData: BarChartData! + { + get + { + return _barData; + } + set + { + _barData = newValue; + for dataSet in newValue.dataSets + { + _dataSets.append(dataSet); + } + + checkIsLegal(newValue.dataSets); + + calcMinMax(); + calcYValueSum(); + calcYValueCount(); + + calcXValAverageLength(); + } + } + + public var scatterData: ScatterChartData! + { + get + { + return _scatterData; + } + set + { + _scatterData = newValue; + for dataSet in newValue.dataSets + { + _dataSets.append(dataSet); + } + + checkIsLegal(newValue.dataSets); + + calcMinMax(); + calcYValueSum(); + calcYValueCount(); + + calcXValAverageLength(); + } + } + + public var candleData: CandleChartData! + { + get + { + return _candleData; + } + set + { + _candleData = newValue; + for dataSet in newValue.dataSets + { + _dataSets.append(dataSet); + } + + checkIsLegal(newValue.dataSets); + + calcMinMax(); + calcYValueSum(); + calcYValueCount(); + + calcXValAverageLength(); + } + } + + public override func notifyDataChanged() + { + _lineData.notifyDataChanged(); + _barData.notifyDataChanged(); + _candleData.notifyDataChanged(); + _scatterData.notifyDataChanged(); + } +} diff --git a/Charts/Classes/Data/LineChartData.swift b/Charts/Classes/Data/LineChartData.swift new file mode 100644 index 0000000000..9ecad10fbd --- /dev/null +++ b/Charts/Classes/Data/LineChartData.swift @@ -0,0 +1,20 @@ +// +// LineChartData.swift +// Charts +// +// Created by Daniel Cohen Gindi on 26/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// Data object that encapsulates all data associated with a LineChart. +public class LineChartData: ChartData +{ + +} diff --git a/Charts/Classes/Data/LineChartDataSet.swift b/Charts/Classes/Data/LineChartDataSet.swift new file mode 100644 index 0000000000..bce103810c --- /dev/null +++ b/Charts/Classes/Data/LineChartDataSet.swift @@ -0,0 +1,116 @@ +// +// LineChartDataSet.swift +// Charts +// +// Created by Daniel Cohen Gindi on 26/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class LineChartDataSet: LineRadarChartDataSet +{ + public var circleColors = [UIColor]() + public var circleHoleColor = UIColor.whiteColor() + public var circleRadius = CGFloat(8.0) + + private var _cubicIntensity = CGFloat(0.2) + + public var lineDashPhase = CGFloat(0.0) + public var lineDashLengths: [CGFloat]! + + /// if true, drawing circles is enabled + public var drawCirclesEnabled = true + + /// if true, cubic lines are drawn instead of linear + public var drawCubicEnabled = false + + public var drawCircleHoleEnabled = true; + + public override init() + { + super.init(); + circleColors.append(UIColor(red: 140.0/255.0, green: 234.0/255.0, blue: 255.0/255.0, alpha: 1.0)); + } + + public override init(yVals: [ChartDataEntry]?, label: String) + { + super.init(yVals: yVals, label: label); + circleColors.append(UIColor(red: 140.0, green: 234.0, blue: 255.0, alpha: 1.0)); + } + + /// intensity for cubic lines (min = 0.05f, max = 1f) + /// :default: 0.2 + public var cubicIntensity: CGFloat + { + get + { + return _cubicIntensity; + } + set + { + _cubicIntensity = newValue; + if (_cubicIntensity > 1.0) + { + _cubicIntensity = 1.0; + } + if (_cubicIntensity < 0.05) + { + _cubicIntensity = 0.05; + } + } + } + + /// Returns the color at the given index of the DataSet's circle-color array. + /// Performs a IndexOutOfBounds check by modulus. + public func getCircleColor(var index: Int) -> UIColor? + { + let size = circleColors.count; + index = index % size; + if (index >= size) + { + return nil; + } + return circleColors[index]; + } + + /// Sets the one and ONLY color that should be used for this DataSet. + /// Internally, this recreates the colors array and adds the specified color. + public func setCircleColor(color: UIColor) + { + circleColors.removeAll(keepCapacity: false); + circleColors.append(color); + } + + /// resets the circle-colors array and creates a new one + public func resetCircleColors(var index: Int) + { + circleColors.removeAll(keepCapacity: false); + } + + public var isDrawCirclesEnabled: Bool { return drawCirclesEnabled; } + + public var isDrawCubicEnabled: Bool { return drawCubicEnabled; } + + public var isDrawCircleHoleEnabled: Bool { return drawCircleHoleEnabled; } + + // MARK: NSCopying + + public override func copyWithZone(zone: NSZone) -> AnyObject + { + var copy = super.copyWithZone(zone) as! LineChartDataSet; + copy.circleColors = circleColors; + copy.circleRadius = circleRadius; + copy.cubicIntensity = cubicIntensity; + copy.lineDashPhase = lineDashPhase; + copy.lineDashLengths = lineDashLengths; + copy.drawCirclesEnabled = drawCirclesEnabled; + copy.drawCubicEnabled = drawCubicEnabled; + return copy; + } +} diff --git a/Charts/Classes/Data/LineRadarChartDataSet.swift b/Charts/Classes/Data/LineRadarChartDataSet.swift new file mode 100644 index 0000000000..fbd632badf --- /dev/null +++ b/Charts/Classes/Data/LineRadarChartDataSet.swift @@ -0,0 +1,60 @@ +// +// LineRadarChartDataSet.swift +// Charts +// +// Created by Daniel Cohen Gindi on 26/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class LineRadarChartDataSet: BarLineScatterCandleChartDataSet +{ + public var fillColor = UIColor(red: 140.0/255.0, green: 234.0/255.0, blue: 255.0/255.0, alpha: 1.0) + public var fillAlpha = CGFloat(0.33) + private var _lineWidth = CGFloat(1.0) + public var drawFilledEnabled = false + + /// line width of the chart (min = 0.2f, max = 10f) + /// :default: 1 + public var lineWidth: CGFloat + { + get + { + return _lineWidth; + } + set + { + _lineWidth = newValue; + if (_lineWidth < 0.2) + { + _lineWidth = 0.5; + } + if (_lineWidth > 10.0) + { + _lineWidth = 10.0; + } + } + } + + public var isDrawFilledEnabled: Bool + { + return drawFilledEnabled; + } + + // MARK: NSCopying + + public override func copyWithZone(zone: NSZone) -> AnyObject + { + var copy = super.copyWithZone(zone) as! LineRadarChartDataSet; + copy.fillColor = fillColor; + copy._lineWidth = _lineWidth; + copy.drawFilledEnabled = drawFilledEnabled; + return copy; + } +} diff --git a/Charts/Classes/Data/PieChartData.swift b/Charts/Classes/Data/PieChartData.swift new file mode 100644 index 0000000000..f1b82d9fbf --- /dev/null +++ b/Charts/Classes/Data/PieChartData.swift @@ -0,0 +1,75 @@ +// +// PieData.swift +// Charts +// +// Created by Daniel Cohen Gindi on 24/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class PieChartData: ChartData +{ + public override init() + { + super.init(); + + } + + var dataSet: PieChartDataSet? + { + get + { + return dataSets.count > 0 ? dataSets[0] as? PieChartDataSet : nil; + } + set + { + if (newValue != nil) + { + dataSets = [newValue!]; + } + else + { + dataSets = []; + } + } + } + + public override func getDataSetByIndex(index: Int) -> ChartDataSet? + { + if (index != 0) + { + return nil; + } + return super.getDataSetByIndex(index); + } + + public override func getDataSetByLabel(label: String, ignorecase: Bool) -> ChartDataSet? + { + if (dataSets.count == 0) + { + return nil; + } + + if (ignorecase) + { + if (label.caseInsensitiveCompare(dataSets[0].label) == NSComparisonResult.OrderedSame) + { + return dataSets[0]; + } + } + else + { + if (label == dataSets[0].label) + { + return dataSets[0]; + } + } + return nil; + } +} diff --git a/Charts/Classes/Data/PieChartDataSet.swift b/Charts/Classes/Data/PieChartDataSet.swift new file mode 100644 index 0000000000..2adcfec7ea --- /dev/null +++ b/Charts/Classes/Data/PieChartDataSet.swift @@ -0,0 +1,71 @@ +// +// PieChartDataSet.swift +// Charts +// +// Created by Daniel Cohen Gindi on 24/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class PieChartDataSet: ChartDataSet +{ + private var _sliceSpace = CGFloat(0.0) + + /// indicates the selection distance of a pie slice + public var selectionShift = CGFloat(18.0) + + public override init() + { + super.init(); + + self.valueTextColor = UIColor.whiteColor(); + self.valueFont = UIFont.systemFontOfSize(13.0); + } + + public override init(yVals: [ChartDataEntry]?, label: String) + { + super.init(yVals: yVals, label: label); + + self.valueTextColor = UIColor.whiteColor(); + self.valueFont = UIFont.systemFontOfSize(13.0); + } + + /// the space that is left out between the piechart-slices, default: 0° + /// --> no space, maximum 45, minimum 0 (no space) + public var sliceSpace: CGFloat + { + get + { + return _sliceSpace; + } + set + { + _sliceSpace = newValue; + if (_sliceSpace > 45.0) + { + _sliceSpace = 45.0; + } + if (_sliceSpace < 0.0) + { + _sliceSpace = 0.0; + } + } + } + + // MARK: NSCopying + + public override func copyWithZone(zone: NSZone) -> AnyObject + { + var copy = super.copyWithZone(zone) as! PieChartDataSet; + copy._sliceSpace = _sliceSpace; + copy.selectionShift = selectionShift; + return copy; + } +} \ No newline at end of file diff --git a/Charts/Classes/Data/RadarChartData.swift b/Charts/Classes/Data/RadarChartData.swift new file mode 100644 index 0000000000..ec3a2a009b --- /dev/null +++ b/Charts/Classes/Data/RadarChartData.swift @@ -0,0 +1,28 @@ +// +// RadarChartData.swift +// Charts +// +// Created by Daniel Cohen Gindi on 26/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class RadarChartData: ChartData +{ + public var highlightColor = UIColor(red: 255.0/255.0, green: 187.0/255.0, blue: 115.0/255.0, alpha: 1.0) + public var highlightLineWidth = CGFloat(1.0) + public var highlightLineDashPhase = CGFloat(0.0) + public var highlightLineDashLengths: [CGFloat]? + + internal override func initialize(dataSets: [ChartDataSet]) + { + super.initialize(dataSets); + + } +} diff --git a/Charts/Classes/Data/RadarChartDataSet.swift b/Charts/Classes/Data/RadarChartDataSet.swift new file mode 100644 index 0000000000..a408c49d49 --- /dev/null +++ b/Charts/Classes/Data/RadarChartDataSet.swift @@ -0,0 +1,32 @@ +// +// RadarChartDataSet.swift +// Charts +// +// Created by Daniel Cohen Gindi on 24/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class RadarChartDataSet: LineRadarChartDataSet +{ + public override init() + { + super.init(); + + self.valueFont = UIFont.systemFontOfSize(13.0); + } + + public override init(yVals: [ChartDataEntry]?, label: String) + { + super.init(yVals: yVals, label: label); + + self.valueFont = UIFont.systemFontOfSize(13.0); + } +} \ No newline at end of file diff --git a/Charts/Classes/Data/ScatterChartData.swift b/Charts/Classes/Data/ScatterChartData.swift new file mode 100644 index 0000000000..667363a6e2 --- /dev/null +++ b/Charts/Classes/Data/ScatterChartData.swift @@ -0,0 +1,44 @@ +// +// ScatterChartData.swift +// Charts +// +// Created by Daniel Cohen Gindi on 26/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ScatterChartData: BarLineScatterCandleChartData +{ + /// Returns the maximum shape-size across all DataSets. + public func getGreatestShapeSize() -> CGFloat + { + var max = CGFloat(0.0); + + for set in _dataSets + { + let scatterDataSet = set as? ScatterChartDataSet; + + if (scatterDataSet == nil) + { + println("ScatterChartData: Found a DataSet which is not a ScatterChartDataSet"); + } + else + { + let size = scatterDataSet!.scatterShapeSize; + + if (size > max) + { + max = size; + } + } + } + + return max; + } +} diff --git a/Charts/Classes/Data/ScatterChartDataSet.swift b/Charts/Classes/Data/ScatterChartDataSet.swift new file mode 100644 index 0000000000..9d87e92405 --- /dev/null +++ b/Charts/Classes/Data/ScatterChartDataSet.swift @@ -0,0 +1,43 @@ +// +// ScatterChartDataSet.swift +// Charts +// +// Created by Daniel Cohen Gindi on 26/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import CoreGraphics; + +public class ScatterChartDataSet: BarLineScatterCandleChartDataSet +{ + @objc + public enum ScatterShape: Int + { + case Cross + case Triangle + case Circle + case Square + case Custom + } + + public var scatterShapeSize = CGFloat(15.0) + public var scatterShape = ScatterShape.Square + public var customScatterShape: CGPath? + + // MARK: NSCopying + + public override func copyWithZone(zone: NSZone) -> AnyObject + { + var copy = super.copyWithZone(zone) as! ScatterChartDataSet; + copy.scatterShapeSize = scatterShapeSize; + copy.scatterShape = scatterShape; + copy.customScatterShape = customScatterShape; + return copy; + } +} diff --git a/Charts/Classes/Filters/ChartDataApproximatorFilter.swift b/Charts/Classes/Filters/ChartDataApproximatorFilter.swift new file mode 100644 index 0000000000..a0b1428e55 --- /dev/null +++ b/Charts/Classes/Filters/ChartDataApproximatorFilter.swift @@ -0,0 +1,216 @@ +// +// ChartDataApproximator.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartDataApproximatorFilter: ChartDataBaseFilter +{ + @objc + public enum ApproximatorType: Int + { + case None + case RamerDouglasPeucker + } + + /// the type of filtering algorithm to use + public var type = ApproximatorType.None + + /// the tolerance to be filtered with + /// When using the Douglas-Peucker-Algorithm, the tolerance is an angle in degrees, that will trigger the filtering + public var tolerance = Double(0.0) + + public var scaleRatio = Float(1.0) + public var deltaRatio = Float(1.0) + + public override init() + { + super.init(); + } + + /// Initializes the approximator with the given type and tolerance. + /// If toleranec <= 0, no filtering will be done. + public init(type: ApproximatorType, tolerance: Double) + { + super.init(); + + setup(type, tolerance: tolerance); + } + + /// Sets type and tolerance. + /// If tolerance <= 0, no filtering will be done. + public func setup(type: ApproximatorType, tolerance: Double) + { + self.type = type; + self.tolerance = tolerance; + } + + /// Sets the ratios for x- and y-axis, as well as the ratio of the scale levels + public func setRatios(deltaRatio: Float, scaleRatio: Float) + { + self.deltaRatio = deltaRatio; + self.scaleRatio = scaleRatio; + } + + /// Filters according to type. Uses the pre set set tolerance + /// + /// :param: points the points to filter + public override func filter(points: [ChartDataEntry]) -> [ChartDataEntry] + { + return filter(points, tolerance: tolerance); + } + + /// Filters according to type. + /// + /// :param: points the points to filter + /// :param: tolerance the angle in degrees that will trigger the filtering + public func filter(points: [ChartDataEntry], tolerance: Double) -> [ChartDataEntry] + { + if (tolerance <= 0) + { + return points; + } + + switch (type) + { + case .RamerDouglasPeucker: + return reduceWithDouglasPeuker(points, epsilon: tolerance); + case .None: + return points; + default: + return points; + } + } + + /// uses the douglas peuker algorithm to reduce the given arraylist of entries + private func reduceWithDouglasPeuker(entries: [ChartDataEntry], epsilon: Double) -> [ChartDataEntry] + { + // if a shape has 2 or less points it cannot be reduced + if (epsilon <= 0 || entries.count < 3) + { + return entries; + } + + var keep = [Bool](count: entries.count, repeatedValue: false); + + // first and last always stay + keep[0] = true; + keep[entries.count - 1] = true; + + // first and last entry are entry point to recursion + algorithmDouglasPeucker(entries, epsilon: epsilon, start: 0, end: entries.count - 1, keep: &keep); + + // create a new array with series, only take the kept ones + var reducedEntries = [ChartDataEntry](); + for (var i = 0; i < entries.count; i++) + { + if (keep[i]) + { + let curEntry = entries[i]; + reducedEntries.append(ChartDataEntry(value: curEntry.value, xIndex: curEntry.xIndex)); + } + } + + return reducedEntries; + } + + /// apply the Douglas-Peucker-Reduction to an ArrayList of Entry with a given epsilon (tolerance) + /// + /// :param: entries + /// :param: epsilon as y-value + /// :param: start + /// :param: end + private func algorithmDouglasPeucker(entries: [ChartDataEntry], epsilon: Double, start: Int, end: Int, inout keep: [Bool]) + { + if (end <= start + 1) + { + // recursion finished + return; + } + + // find the greatest distance between start and endpoint + var maxDistIndex = Int(0); + var distMax = Double(0.0); + + var firstEntry = entries[start]; + var lastEntry = entries[end]; + + for (var i = start + 1; i < end; i++) + { + var dist = calcAngleBetweenLines(firstEntry, end1: lastEntry, start2: firstEntry, end2: entries[i]); + + // keep the point with the greatest distance + if (dist > distMax) + { + distMax = dist; + maxDistIndex = i; + } + } + + if (distMax > epsilon) + { + // keep max dist point + keep[maxDistIndex] = true; + + // recursive call + algorithmDouglasPeucker(entries, epsilon: epsilon, start: start, end: maxDistIndex, keep: &keep); + algorithmDouglasPeucker(entries, epsilon: epsilon, start: maxDistIndex, end: end, keep: &keep); + } // else don't keep the point... + } + + /// calculate the distance between a line between two entries and an entry (point) + /// + /// :param: startEntry line startpoint + /// :param: endEntry line endpoint + /// :param: entryPoint the point to which the distance is measured from the line + private func calcPointToLineDistance(startEntry: ChartDataEntry, endEntry: ChartDataEntry, entryPoint: ChartDataEntry) -> Double + { + var xDiffEndStart = Float(endEntry.xIndex) - Float(startEntry.xIndex); + var xDiffEntryStart = Float(entryPoint.xIndex) - Float(startEntry.xIndex); + + var normalLength = sqrt((xDiffEndStart) + * (xDiffEndStart) + + (endEntry.value - startEntry.value) + * (endEntry.value - startEntry.value)); + + return Double(fabs((xDiffEntryStart) + * (endEntry.value - startEntry.value) + - (entryPoint.value - startEntry.value) + * (xDiffEndStart))) / Double(normalLength); + } + + /// Calculates the angle between two given lines. The provided entries mark the starting and end points of the lines. + private func calcAngleBetweenLines(start1: ChartDataEntry, end1: ChartDataEntry, start2: ChartDataEntry, end2: ChartDataEntry) -> Double + { + var angle1 = calcAngleWithRatios(start1, p2: end1); + var angle2 = calcAngleWithRatios(start2, p2: end2); + + return fabs(angle1 - angle2); + } + + /// calculates the angle between two entries (points) in the chart taking ratios into consideration + private func calcAngleWithRatios(p1: ChartDataEntry, p2: ChartDataEntry) -> Double + { + var dx = Double(p2.xIndex) * Double(deltaRatio) - Double(p1.xIndex) * Double(deltaRatio); + var dy = p2.value * scaleRatio - p1.value * scaleRatio; + return atan2(Double(dy), dx) * ChartUtils.Math.RAD2DEG; + } + + // calculates the angle between two entries (points) in the chart + private func calcAngle(p1: ChartDataEntry, p2: ChartDataEntry) -> Double + { + var dx = p2.xIndex - p1.xIndex; + var dy = p2.value - p1.value; + return atan2(Double(dy), Double(dx)) * ChartUtils.Math.RAD2DEG; + } +} \ No newline at end of file diff --git a/Charts/Classes/Filters/ChartDataBaseFilter.swift b/Charts/Classes/Filters/ChartDataBaseFilter.swift new file mode 100644 index 0000000000..b60561a5ae --- /dev/null +++ b/Charts/Classes/Filters/ChartDataBaseFilter.swift @@ -0,0 +1,28 @@ +// +// ChartDataFilter.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartDataBaseFilter: NSObject +{ + public override init() + { + super.init(); + } + + public func filter(points: [ChartDataEntry]) -> [ChartDataEntry] + { + fatalError("filter() cannot be called on ChartDataBaseFilter"); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/BarChartRenderer.swift b/Charts/Classes/Renderers/BarChartRenderer.swift new file mode 100644 index 0000000000..e85fd28616 --- /dev/null +++ b/Charts/Classes/Renderers/BarChartRenderer.swift @@ -0,0 +1,540 @@ +// +// BarChartRenderer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +@objc +public protocol BarChartRendererDelegate +{ + func barChartRendererData(renderer: BarChartRenderer) -> BarChartData!; + func barChartRenderer(renderer: BarChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer!; + func barChartRendererMaxVisibleValueCount(renderer: BarChartRenderer) -> Int; + func barChartDefaultRendererValueFormatter(renderer: BarChartRenderer) -> NSNumberFormatter!; + func barChartRendererChartXMax(renderer: BarChartRenderer) -> Float; + func barChartIsDrawHighlightArrowEnabled(renderer: BarChartRenderer) -> Bool; + func barChartIsDrawValueAboveBarEnabled(renderer: BarChartRenderer) -> Bool; + func barChartIsDrawValuesForWholeStackEnabled(renderer: BarChartRenderer) -> Bool; + func barChartIsDrawBarShadowEnabled(renderer: BarChartRenderer) -> Bool; +} + +public class BarChartRenderer: ChartDataRendererBase +{ + public weak var delegate: BarChartRendererDelegate?; + + public init(delegate: BarChartRendererDelegate?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + { + super.init(animator: animator, viewPortHandler: viewPortHandler); + + self.delegate = delegate; + } + + public override func drawData(#context: CGContext) + { + var barData = delegate!.barChartRendererData(self); + + if (barData === nil) + { + return; + } + + for (var i = 0; i < barData.dataSetCount; i++) + { + var set = barData.getDataSetByIndex(i); + + if (set !== nil && set!.isVisible) + { + drawDataSet(context: context, dataSet: set as! BarChartDataSet, index: i); + } + } + } + + internal func drawDataSet(#context: CGContext, dataSet: BarChartDataSet, index: Int) + { + CGContextSaveGState(context); + + var barData = delegate!.barChartRendererData(self); + + var trans = delegate!.barChartRenderer(self, transformerForAxis: dataSet.axisDependency); + calcXBounds(trans); + + var drawBarShadowEnabled: Bool = delegate!.barChartIsDrawBarShadowEnabled(self); + var dataSetOffset = (barData.dataSetCount - 1); + var groupSpace = barData.groupSpace; + var groupSpaceHalf = groupSpace / 2.0; + var barSpace = dataSet.barSpace; + var barSpaceHalf = barSpace / 2.0; + var containsStacks = dataSet.isStacked; + var entries = dataSet.yVals as! [BarChartDataEntry]; + var barWidth: CGFloat = 0.5; + var phaseY = _animator.phaseY; + var barRect = CGRect(); + var barShadow = CGRect(); + var y: Float; + + // do the drawing + for (var j = 0, count = Int(ceil(CGFloat(dataSet.entryCount) * _animator.phaseX)); j < count; j++) + { + var e = entries[j]; + + // calculate the x-position, depending on datasetcount + var x = CGFloat(e.xIndex + j * dataSetOffset) + CGFloat(index) + + groupSpace * CGFloat(j) + groupSpaceHalf; + var vals = e.values; + + if (!containsStacks || vals == nil) + { + y = e.value; + + var left = x - barWidth + barSpaceHalf; + var right = x + barWidth - barSpaceHalf; + var top = y >= 0.0 ? CGFloat(y) : 0; + var bottom = y <= 0.0 ? CGFloat(y) : 0; + + // multiply the height of the rect with the phase + if (top > 0) + { + top *= phaseY; + } + else + { + bottom *= phaseY; + } + + barRect.origin.x = left; + barRect.size.width = right - left; + barRect.origin.y = top; + barRect.size.height = bottom - top; + + trans.rectValueToPixel(&barRect); + + if (!viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width)) + { + continue; + } + + if (!viewPortHandler.isInBoundsRight(barRect.origin.x)) + { + break; + } + + // if drawing the bar shadow is enabled + if (drawBarShadowEnabled) + { + barShadow.origin.x = barRect.origin.x; + barShadow.origin.y = viewPortHandler.contentTop; + barShadow.size.width = barRect.size.width; + barShadow.size.height = viewPortHandler.contentHeight; + + CGContextSetFillColorWithColor(context, dataSet.barShadowColor.CGColor); + CGContextFillRect(context, barShadow); + } + + // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. + CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor); + CGContextFillRect(context, barRect); + } + else + { + var all = e.value; + + // if drawing the bar shadow is enabled + if (drawBarShadowEnabled) + { + y = e.value; + + var left = x - barWidth + barSpaceHalf; + var right = x + barWidth - barSpaceHalf; + var top = y >= 0.0 ? CGFloat(y) : 0; + var bottom = y <= 0.0 ? CGFloat(y) : 0; + + // multiply the height of the rect with the phase + if (top > 0) + { + top *= phaseY; + } + else + { + bottom *= phaseY; + } + + barRect.origin.x = left; + barRect.size.width = right - left; + barRect.origin.y = top; + barRect.size.height = bottom - top; + + trans.rectValueToPixel(&barRect); + + barShadow.origin.x = barRect.origin.x; + barShadow.origin.y = viewPortHandler.contentTop; + barShadow.size.width = barRect.size.width; + barShadow.size.height = viewPortHandler.contentHeight; + + CGContextSetFillColorWithColor(context, dataSet.barShadowColor.CGColor); + CGContextFillRect(context, barShadow); + } + + // fill the stack + for (var k = 0; k < vals.count; k++) + { + all -= vals[k]; + y = vals[k] + all; + + var left = x - barWidth + barSpaceHalf; + var right = x + barWidth - barSpaceHalf; + var top = y >= 0.0 ? CGFloat(y) : 0; + var bottom = y <= 0.0 ? CGFloat(y) : 0; + + // multiply the height of the rect with the phase + if (top > 0) + { + top *= phaseY; + } + else + { + bottom *= phaseY; + } + + barRect.origin.x = left; + barRect.size.width = right - left; + barRect.origin.y = top; + barRect.size.height = bottom - top; + + trans.rectValueToPixel(&barRect); + + if (k == 0 && !viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width)) + { + // Skip to next bar + break; + } + + // avoid drawing outofbounds values + if (!viewPortHandler.isInBoundsRight(barRect.origin.x)) + { + break; + } + + // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. + CGContextSetFillColorWithColor(context, dataSet.colorAt(k).CGColor); + CGContextFillRect(context, barRect); + } + } + } + + CGContextRestoreGState(context); + } + + /// Prepares a bar for being highlighted. + internal func prepareBarHighlight(#x: CGFloat, y: Float, barspace: CGFloat, from: Float, trans: ChartTransformer, inout rect: CGRect) + { + var barWidth: CGFloat = 0.5; + + var spaceHalf = barspace / 2.0; + var left = x - barWidth + spaceHalf; + var right = x + barWidth - spaceHalf; + var top = y >= from ? CGFloat(y) : CGFloat(from); + var bottom = y <= from ? CGFloat(y) : CGFloat(from); + + rect.origin.x = left; + rect.origin.y = top; + rect.size.width = right - left; + rect.size.height = bottom - top; + + trans.rectValueToPixel(&rect, phaseY: _animator.phaseY); + } + + public override func drawValues(#context: CGContext) + { + // if values are drawn + if (passesCheck()) + { + var barData = delegate!.barChartRendererData(self); + + var defaultValueFormatter = delegate!.barChartDefaultRendererValueFormatter(self); + + var dataSets = barData.dataSets; + + var drawValueAboveBar = delegate!.barChartIsDrawValueAboveBarEnabled(self); + var drawValuesForWholeStackEnabled = delegate!.barChartIsDrawValuesForWholeStackEnabled(self); + + var valueTextHeight: CGFloat; + var posOffset: CGFloat; + var negOffset: CGFloat; + + for (var i = 0, count = barData.dataSetCount; i < count; i++) + { + var dataSet = dataSets[i]; + + if (!dataSet.isDrawValuesEnabled) + { + continue; + } + + // calculate the correct offset depending on the draw position of the value + let plus: CGFloat = 6.0; + var valueFont = dataSet.valueFont; + var valueTextHeight = valueFont.lineHeight; + var posOffset = (drawValueAboveBar ? -(valueTextHeight + plus) : plus); + var negOffset = (drawValueAboveBar ? plus : -(valueTextHeight + plus)); + + var valueTextColor = dataSet.valueTextColor; + + var formatter = dataSet.valueFormatter; + if (formatter === nil) + { + formatter = defaultValueFormatter; + } + + var trans = delegate!.barChartRenderer(self, transformerForAxis: dataSet.axisDependency); + + var entries = dataSet.yVals as! [BarChartDataEntry]; + + var valuePoints = getTransformedValues(trans: trans, entries: entries, dataSetIndex: i); + + // if only single values are drawn (sum) + if (!drawValuesForWholeStackEnabled) + { + for (var j = 0, count = Int(ceil(CGFloat(valuePoints.count) * _animator.phaseX)); j < count; j++) + { + if (!viewPortHandler.isInBoundsRight(valuePoints[j].x)) + { + break; + } + + if (!viewPortHandler.isInBoundsY(valuePoints[j].y) + || !viewPortHandler.isInBoundsLeft(valuePoints[j].x)) + { + continue; + } + + var val = entries[j].value; + + drawValue(context: context, + val: val, + xPos: valuePoints[j].x, + yPos: valuePoints[j].y + (val >= 0.0 ? posOffset : negOffset), + formatter: formatter!, + font: valueFont, + align: .Center, + color: valueTextColor); + } + } + else + { + // if each value of a potential stack should be drawn + + for (var j = 0, count = Int(ceil(CGFloat(valuePoints.count) * _animator.phaseX)); j < count; j++) + { + var e = entries[j]; + + var vals = e.values; + + // we still draw stacked bars, but there is one non-stacked in between + if (vals == nil) + { + if (!viewPortHandler.isInBoundsRight(valuePoints[j].x)) + { + break; + } + + if (!viewPortHandler.isInBoundsY(valuePoints[j].y) + || !viewPortHandler.isInBoundsLeft(valuePoints[j].x)) + { + continue; + } + + drawValue(context: context, + val: e.value, + xPos: valuePoints[j].x, + yPos: valuePoints[j].y + (e.value >= 0.0 ? posOffset : negOffset), + formatter: formatter!, + font: valueFont, + align: .Center, + color: valueTextColor); + } + else + { + var transformed = [CGPoint](); + var cnt = 0; + var add = e.value; + + for (var k = 0; k < vals.count; k++) + { + add -= vals[cnt]; + transformed.append(CGPoint(x: 0.0, y: (CGFloat(vals[cnt]) + CGFloat(add)) * _animator.phaseY)); + cnt++; + } + + trans.pointValuesToPixel(&transformed); + + for (var k = 0; k < transformed.count; k++) + { + var x = valuePoints[j].x; + var y = transformed[k].y + (vals[k] >= 0 ? posOffset : negOffset); + + if (!viewPortHandler.isInBoundsRight(x)) + { + break; + } + + if (!viewPortHandler.isInBoundsY(y) || !viewPortHandler.isInBoundsLeft(x)) + { + continue; + } + + drawValue(context: context, + val: vals[k], + xPos: x, + yPos: y, + formatter: formatter!, + font: valueFont, + align: .Center, + color: valueTextColor); + } + } + } + } + } + } + } + + /// Draws a value at the specified x and y position. + internal func drawValue(#context: CGContext, val: Float, xPos: CGFloat, yPos: CGFloat, formatter: NSNumberFormatter, font: UIFont, align: NSTextAlignment, color: UIColor) + { + var value = formatter.stringFromNumber(val)!; + ChartUtils.drawText(context: context, text: value, point: CGPoint(x: xPos, y: yPos), align: align, attributes: [NSFontAttributeName: font, NSForegroundColorAttributeName: color]); + } + + public override func drawExtras(#context: CGContext) + { + + } + + private var _highlightArrowPtsBuffer = [CGPoint](count: 3, repeatedValue: CGPoint()); + + public override func drawHighlighted(#context: CGContext, indices: [ChartHighlight]) + { + var barData = delegate!.barChartRendererData(self); + if (barData === nil) + { + return; + } + + CGContextSaveGState(context); + + var setCount = barData.dataSetCount; + var drawHighlightArrowEnabled = delegate!.barChartIsDrawHighlightArrowEnabled(self); + var barRect = CGRect(); + + for (var i = 0; i < indices.count; i++) + { + var h = indices[i]; + var index = h.xIndex; + + var dataSetIndex = h.dataSetIndex; + var set = barData.getDataSetByIndex(dataSetIndex) as! BarChartDataSet!; + + if (set === nil) + { + continue; + } + + var trans = delegate!.barChartRenderer(self, transformerForAxis: set.axisDependency); + + CGContextSetFillColorWithColor(context, set.highlightColor.CGColor); + CGContextSetAlpha(context, set.highLightAlpha); + + // check outofbounds + if (index < barData.yValCount && index >= 0 + && CGFloat(index) < (CGFloat(delegate!.barChartRendererChartXMax(self)) * _animator.phaseX) / CGFloat(setCount)) + { + var e = barData.getDataSetByIndex(dataSetIndex)!.entryForXIndex(index) as! BarChartDataEntry!; + + if (e === nil) + { + continue; + } + + var groupspace = barData.groupSpace; + var isStack = h.stackIndex < 0 ? false : true; + + // calculate the correct x-position + var x = CGFloat(index * setCount + dataSetIndex) + groupspace / 2.0 + groupspace * CGFloat(index); + var y = isStack ? e.values[h.stackIndex] + e.getBelowSum(h.stackIndex) : e.value; + + // this is where the bar starts + var from = isStack ? e.getBelowSum(h.stackIndex) : 0.0; + + prepareBarHighlight(x: x, y: y, barspace: set.barSpace, from: from, trans: trans, rect: &barRect); + + CGContextFillRect(context, barRect); + + if (drawHighlightArrowEnabled) + { + CGContextSetAlpha(context, 1.0); + + // distance between highlight arrow and bar + var offsetY = _animator.phaseY * 0.07; + + CGContextSaveGState(context); + + var pixelToValueMatrix = trans.pixelToValueMatrix; + var onePoint = CGPoint(x: 100.0, y: 100.0); + onePoint.x = onePoint.x * sqrt(pixelToValueMatrix.a * pixelToValueMatrix.a + pixelToValueMatrix.c * pixelToValueMatrix.c); + onePoint.y = onePoint.y * sqrt(pixelToValueMatrix.b * pixelToValueMatrix.b + pixelToValueMatrix.d * pixelToValueMatrix.d); + var xToYRel = abs(onePoint.y / onePoint.x); + + var arrowWidth = set.barSpace / 2.0; + var arrowHeight = arrowWidth * xToYRel; + + _highlightArrowPtsBuffer[0].x = CGFloat(x) + 0.4; + _highlightArrowPtsBuffer[0].y = CGFloat(y) + offsetY; + _highlightArrowPtsBuffer[1].x = CGFloat(x) + 0.4 + arrowWidth; + _highlightArrowPtsBuffer[1].y = CGFloat(y) + offsetY - arrowHeight; + _highlightArrowPtsBuffer[2].x = CGFloat(x) + 0.4 + arrowWidth; + _highlightArrowPtsBuffer[2].y = CGFloat(y) + offsetY + arrowHeight; + + trans.pointValuesToPixel(&_highlightArrowPtsBuffer); + + CGContextBeginPath(context); + CGContextMoveToPoint(context, _highlightArrowPtsBuffer[0].x, _highlightArrowPtsBuffer[0].y); + CGContextAddLineToPoint(context, _highlightArrowPtsBuffer[1].x, _highlightArrowPtsBuffer[1].y); + CGContextAddLineToPoint(context, _highlightArrowPtsBuffer[2].x, _highlightArrowPtsBuffer[2].y); + CGContextClosePath(context); + + CGContextFillPath(context); + + CGContextRestoreGState(context); + } + } + } + + CGContextRestoreGState(context); + } + + public func getTransformedValues(#trans: ChartTransformer, entries: [BarChartDataEntry], dataSetIndex: Int) -> [CGPoint] + { + return trans.generateTransformedValuesBarChart(entries, dataSet: dataSetIndex, barData: delegate!.barChartRendererData(self)!, phaseY: _animator.phaseY); + } + + internal func passesCheck() -> Bool + { + var barData = delegate!.barChartRendererData(self); + + if (barData === nil) + { + return false; + } + + return CGFloat(barData.yValCount) < CGFloat(delegate!.barChartRendererMaxVisibleValueCount(self)) * viewPortHandler.scaleX; + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/CandleStickChartRenderer.swift b/Charts/Classes/Renderers/CandleStickChartRenderer.swift new file mode 100644 index 0000000000..6ff7c794d8 --- /dev/null +++ b/Charts/Classes/Renderers/CandleStickChartRenderer.swift @@ -0,0 +1,264 @@ +// +// CandleStickChartRenderer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +@objc +public protocol CandleStickChartRendererDelegate +{ + func candleStickChartRendererCandleData(renderer: CandleStickChartRenderer) -> CandleChartData!; + func candleStickChartRenderer(renderer: CandleStickChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer!; + func candleStickChartDefaultRendererValueFormatter(renderer: CandleStickChartRenderer) -> NSNumberFormatter!; + func candleStickChartRendererChartYMax(renderer: CandleStickChartRenderer) -> Float; + func candleStickChartRendererChartYMin(renderer: CandleStickChartRenderer) -> Float; + func candleStickChartRendererChartXMax(renderer: CandleStickChartRenderer) -> Float; + func candleStickChartRendererChartXMin(renderer: CandleStickChartRenderer) -> Float; + func candleStickChartRendererMaxVisibleValueCount(renderer: CandleStickChartRenderer) -> Int; +} + +public class CandleStickChartRenderer: ChartDataRendererBase +{ + public weak var delegate: CandleStickChartRendererDelegate?; + + public init(delegate: CandleStickChartRendererDelegate?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + { + super.init(animator: animator, viewPortHandler: viewPortHandler); + + self.delegate = delegate; + } + + public override func drawData(#context: CGContext) + { + var candleData = delegate!.candleStickChartRendererCandleData(self); + + for set in candleData.dataSets as! [CandleChartDataSet] + { + if (set.isVisible) + { + drawDataSet(context: context, dataSet: set); + } + } + } + + private var _shadowPoints = [CGPoint](count: 2, repeatedValue: CGPoint()); + private var _bodyRect = CGRect(); + private var _lineSegments = [CGPoint](count: 2, repeatedValue: CGPoint()); + + internal func drawDataSet(#context: CGContext, dataSet: CandleChartDataSet) + { + var candleData = delegate!.candleStickChartRendererCandleData(self); + + var trans = delegate!.candleStickChartRenderer(self, transformerForAxis: dataSet.axisDependency); + calcXBounds(trans); + + var phaseX = _animator.phaseX; + var phaseY = _animator.phaseY; + var bodySpace = dataSet.bodySpace; + + var dataSetIndex = candleData.indexOfDataSet(dataSet); + + var entries = dataSet.yVals as! [CandleChartDataEntry]; + + CGContextSaveGState(context); + + CGContextSetLineWidth(context, dataSet.shadowWidth); + + for (var j = 0, count = Int(ceil(CGFloat(entries.count) * _animator.phaseX)); j < count; j++) + { + // get the color that is specified for this position from the DataSet, this will reuse colors, if the index is out of bounds + CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor); + CGContextSetStrokeColorWithColor(context, dataSet.colorAt(j).CGColor); + + // get the entry + var e = entries[j]; + + if (e.xIndex < _minX || e.xIndex > _maxX) + { + continue; + } + + // calculate the shadow + + _shadowPoints[0].x = CGFloat(e.xIndex); + _shadowPoints[0].y = CGFloat(e.high) * phaseY; + _shadowPoints[1].x = CGFloat(e.xIndex); + _shadowPoints[1].y = CGFloat(e.low) * phaseY; + + trans.pointValuesToPixel(&_shadowPoints); + + // draw the shadow + + CGContextStrokeLineSegments(context, _shadowPoints, 2); + + // calculate the body + + _bodyRect.origin.x = CGFloat(e.xIndex) - 0.5 + bodySpace; + _bodyRect.origin.y = CGFloat(e.close) * phaseY; + _bodyRect.size.width = (CGFloat(e.xIndex) + 0.5 - bodySpace) - _bodyRect.origin.x; + _bodyRect.size.height = (CGFloat(e.open) * phaseY) - _bodyRect.origin.y; + + trans.rectValueToPixel(&_bodyRect); + + // decide whether the body is hollow or filled + if (_bodyRect.size.height > 0.0) + { + // draw the body + CGContextFillRect(context, _bodyRect); + } + else + { + // draw the body + CGContextStrokeRect(context, _bodyRect); + } + } + + CGContextRestoreGState(context); + } + + public override func drawValues(#context: CGContext) + { + var candleData = delegate!.candleStickChartRendererCandleData(self); + if (candleData === nil) + { + return; + } + + var defaultValueFormatter = delegate!.candleStickChartDefaultRendererValueFormatter(self); + + // if values are drawn + if (candleData.yValCount < Int(ceil(CGFloat(delegate!.candleStickChartRendererMaxVisibleValueCount(self)) * viewPortHandler.scaleX))) + { + var dataSets = candleData.dataSets; + + for (var i = 0; i < dataSets.count; i++) + { + var dataSet = dataSets[i]; + + if (!dataSet.isDrawValuesEnabled) + { + continue; + } + + var valueFont = dataSet.valueFont; + var valueTextColor = dataSet.valueTextColor; + + var formatter = dataSet.valueFormatter; + if (formatter === nil) + { + formatter = defaultValueFormatter; + } + + var trans = delegate!.candleStickChartRenderer(self, transformerForAxis: dataSet.axisDependency); + + var entries = dataSet.yVals as! [CandleChartDataEntry]; + + var positions = trans.generateTransformedValuesCandle(entries, phaseY: _animator.phaseY); + + var lineHeight = valueFont.lineHeight; + var yOffset: CGFloat = lineHeight + 5.0; + + for (var j = 0, count = Int(ceil(CGFloat(positions.count) * _animator.phaseX)); j < count; j++) + { + var x = positions[j].x; + var y = positions[j].y; + + if (!viewPortHandler.isInBoundsRight(x)) + { + break; + } + + if (!viewPortHandler.isInBoundsLeft(x) || !viewPortHandler.isInBoundsY(y)) + { + continue; + } + + var val = entries[j].high; + + ChartUtils.drawText(context: context, text: formatter!.stringFromNumber(val)!, point: CGPoint(x: x, y: y - yOffset), align: .Center, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: valueTextColor]); + } + } + } + } + + public override func drawExtras(#context: CGContext) + { + } + + private var _vertPtsBuffer = [CGPoint](count: 4, repeatedValue: CGPoint()); + private var _horzPtsBuffer = [CGPoint](count: 4, repeatedValue: CGPoint()); + public override func drawHighlighted(#context: CGContext, indices: [ChartHighlight]) + { + var candleData = delegate!.candleStickChartRendererCandleData(self); + if (candleData === nil) + { + return; + } + + for (var i = 0; i < indices.count; i++) + { + var xIndex = indices[i].xIndex; // get the x-position + + var set = candleData.getDataSetByIndex(indices[i].dataSetIndex) as! CandleChartDataSet!; + + if (set === nil) + { + continue; + } + + var e = set.entryForXIndex(xIndex) as! CandleChartDataEntry!; + + if (e === nil) + { + continue; + } + + var trans = delegate!.candleStickChartRenderer(self, transformerForAxis: set.axisDependency); + + CGContextSetStrokeColorWithColor(context, set.highlightColor.CGColor); + CGContextSetLineWidth(context, set.highlightLineWidth); + if (set.highlightLineDashLengths != nil) + { + CGContextSetLineDash(context, set.highlightLineDashPhase, set.highlightLineDashLengths!, set.highlightLineDashLengths!.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var low = CGFloat(e.low) * _animator.phaseY; + var high = CGFloat(e.high) * _animator.phaseY; + + var min = delegate!.candleStickChartRendererChartYMin(self); + var max = delegate!.candleStickChartRendererChartYMax(self); + + _vertPtsBuffer[0] = CGPoint(x: CGFloat(xIndex) - 0.5, y: CGFloat(max)); + _vertPtsBuffer[1] = CGPoint(x: CGFloat(xIndex) - 0.5, y: CGFloat(min)); + _vertPtsBuffer[2] = CGPoint(x: CGFloat(xIndex) + 0.5, y: CGFloat(max)); + _vertPtsBuffer[3] = CGPoint(x: CGFloat(xIndex) + 0.5, y: CGFloat(min)); + + _horzPtsBuffer[0] = CGPoint(x: CGFloat(0.0), y: low); + _horzPtsBuffer[1] = CGPoint(x: CGFloat(delegate!.candleStickChartRendererChartXMax(self)), y: low); + _horzPtsBuffer[2] = CGPoint(x: 0.0, y: high); + _horzPtsBuffer[3] = CGPoint(x: CGFloat(delegate!.candleStickChartRendererChartXMax(self)), y: high); + + trans.pointValuesToPixel(&_vertPtsBuffer); + trans.pointValuesToPixel(&_horzPtsBuffer); + + // draw the vertical highlight lines + CGContextStrokeLineSegments(context, _vertPtsBuffer, 4); + + // draw the horizontal highlight lines + CGContextStrokeLineSegments(context, _horzPtsBuffer, 4); + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartAxisRendererBase.swift b/Charts/Classes/Renderers/ChartAxisRendererBase.swift new file mode 100644 index 0000000000..07544f6292 --- /dev/null +++ b/Charts/Classes/Renderers/ChartAxisRendererBase.swift @@ -0,0 +1,50 @@ +// +// ChartAxisRendererBase.swift +// Charts +// +// Created by Daniel Cohen Gindi on 3/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit + +public class ChartAxisRendererBase: ChartRendererBase +{ + internal var transformer: ChartTransformer!; + + public override init() + { + super.init(); + } + + public init(viewPortHandler: ChartViewPortHandler, transformer: ChartTransformer!) + { + super.init(viewPortHandler: viewPortHandler); + + self.transformer = transformer; + } + + /// Draws the axis labels on the specified context + public func renderAxisLabels(#context: CGContext) + { + fatalError("renderAxisLabels() cannot be called on ChartAxisRendererBase"); + } + + /// Draws the grid lines belonging to the axis. + public func renderGridLines(#context: CGContext) + { + fatalError("renderGridLines() cannot be called on ChartAxisRendererBase"); + } + + /// Draws the line that goes alongside the axis. + internal func renderAxisLine(#context: CGContext) + { + fatalError("renderAxisLine() cannot be called on ChartAxisRendererBase"); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartDataRendererBase.swift b/Charts/Classes/Renderers/ChartDataRendererBase.swift new file mode 100644 index 0000000000..93d7a23a52 --- /dev/null +++ b/Charts/Classes/Renderers/ChartDataRendererBase.swift @@ -0,0 +1,45 @@ +// +// ChartDataRendererBase.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartDataRendererBase: ChartRendererBase +{ + internal var _animator: ChartAnimator!; + + public init(animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + { + super.init(viewPortHandler: viewPortHandler); + _animator = animator; + } + + public func drawData(#context: CGContext) + { + fatalError("drawData() cannot be called on ChartDataRendererBase"); + } + + public func drawValues(#context: CGContext) + { + fatalError("drawValues() cannot be called on ChartDataRendererBase"); + } + + public func drawExtras(#context: CGContext) + { + fatalError("drawExtras() cannot be called on ChartDataRendererBase"); + } + + public func drawHighlighted(#context: CGContext, indices: [ChartHighlight]) + { + fatalError("drawHighlighted() cannot be called on ChartDataRendererBase"); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartLegendRenderer.swift b/Charts/Classes/Renderers/ChartLegendRenderer.swift new file mode 100644 index 0000000000..ee8f8ce6d4 --- /dev/null +++ b/Charts/Classes/Renderers/ChartLegendRenderer.swift @@ -0,0 +1,432 @@ +// +// ChartLegendRenderer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import CoreGraphics; + +public class ChartLegendRenderer: ChartRendererBase +{ + public override init(viewPortHandler: ChartViewPortHandler) + { + super.init(viewPortHandler: viewPortHandler); + } + + /// Prepares the legend and calculates all needed forms and colors. + public func computeLegend(data: ChartData, legend: ChartLegend!) -> ChartLegend + { + var labels = [String?](); + var colors = [UIColor?](); + + // loop for building up the colors and labels used in the legend + for (var i = 0, count = data.dataSetCount; i < count; i++) + { + var dataSet = data.getDataSetByIndex(i)!; + + var clrs: [UIColor] = dataSet.colors; + var entryCount = dataSet.entryCount; + + // if we have a barchart with stacked bars + if (dataSet.isKindOfClass(BarChartDataSet) && (dataSet as! BarChartDataSet).stackSize > 1) + { + var bds = dataSet as! BarChartDataSet; + var sLabels = bds.stackLabels; + + for (var j = 0; j < clrs.count && j < bds.stackSize; j++) + { + labels.append(sLabels[j % sLabels.count]); + colors.append(clrs[j]); + } + + // add the legend description label + colors.append(UIColor.clearColor()); + labels.append(bds.label); + + } + else if (dataSet.isKindOfClass(PieChartDataSet)) + { + var xVals = data.xVals; + var pds = dataSet as! PieChartDataSet; + + for (var j = 0; j < clrs.count && j < entryCount && j < xVals.count; j++) + { + labels.append(xVals[j]); + colors.append(clrs[j]); + } + + // add the legend description label + colors.append(UIColor.clearColor()); + labels.append(pds.label); + } + else + { // all others + + for (var j = 0; j < clrs.count && j < entryCount; j++) + { + // if multiple colors are set for a DataSet, group them + if (j < clrs.count - 1 && j < entryCount - 1) + { + labels.append(nil); + } + else + { // add label to the last entry + labels.append(dataSet.label); + } + + colors.append(clrs[j]); + } + } + } + + var l = ChartLegend(colors: colors, labels: labels); + + if (legend !== nil) + { + // apply the old legend settings to a potential new legend + l.apply(legend); + } + + // calculate all dimensions of the legend + l.calculateDimensions(l.font); + + return l; + } + + public func renderLegend(#context: CGContext, legend: ChartLegend!) + { + if (legend === nil || !legend.enabled) + { + return; + } + + var labelFont = legend.font; + var labelTextColor = legend.textColor; + var labelLineHeight = labelFont.lineHeight; + + var labels = legend.labels; + + var formSize = legend.formSize; + + // space between text and shape/form of entry + var formTextSpaceAndForm = legend.formToTextSpace + formSize; + + // space between the entries + var stackSpace = legend.stackSpace; + + // the amount of pixels the text needs to be set down to be on the same height as the form + var textDrop = (labelFont.lineHeight + formSize) / 2.0; + + // contains the stacked legend size in pixels + var stack = CGFloat(0.0); + + var wasStacked = false; + + var yoffset = legend.yOffset; + var xoffset = legend.xOffset; + + switch (legend.position) + { + case .BelowChartLeft: + + var posX = viewPortHandler.contentLeft + xoffset; + var posY = viewPortHandler.chartHeight - yoffset; + + for (var i = 0; i < labels.count; i++) + { + drawForm(context, x: posX, y: posY - legend.textHeightMax / 2.0, colorIndex: i, legend: legend); + + // grouped forms have null labels + if (labels[i] != nil) + { + // make a step to the left + if (legend.colors[i] != UIColor.clearColor()) + { + posX += formTextSpaceAndForm; + } + + drawLabel(context, x: posX, y: posY - legend.textHeightMax, label: legend.getLabel(i)!, font: labelFont, textColor: labelTextColor); + posX += (labels[i] as NSString!).sizeWithAttributes([NSFontAttributeName: labelFont]).width + legend.xEntrySpace; + } + else + { + posX += formSize + stackSpace; + } + } + + break; + case .BelowChartRight: + + var posX = viewPortHandler.contentRight - xoffset; + var posY = viewPortHandler.chartHeight - yoffset; + + for (var i = labels.count - 1; i >= 0; i--) + { + if (labels[i] != nil) + { + posX -= (labels[i] as NSString!).sizeWithAttributes([NSFontAttributeName: labelFont]).width + legend.xEntrySpace; + drawLabel(context, x: posX, y: posY - legend.textHeightMax, label: legend.getLabel(i)!, font: labelFont, textColor: labelTextColor); + if (legend.colors[i] != UIColor.clearColor()) + { + posX -= formTextSpaceAndForm; + } + } + else + { + posX -= stackSpace + formSize; + } + + drawForm(context, x: posX, y: posY - legend.textHeightMax / 2.0, colorIndex: i, legend: legend); + } + + break; + case .RightOfChart: + + var posX = viewPortHandler.chartWidth - legend.textWidthMax - xoffset; + var posY = viewPortHandler.contentTop + yoffset; + + for (var i = 0; i < labels.count; i++) + { + drawForm(context, x: posX + stack, y: posY, colorIndex: i, legend: legend); + + if (labels[i] != nil) + { + if (!wasStacked) + { + var x = posX; + + if (legend.colors[i] != UIColor.clearColor()) + { + x += formTextSpaceAndForm; + } + + drawLabel(context, x: x, y: posY - legend.textHeightMax / 2.0, label: legend.getLabel(i)!, font: labelFont, textColor: labelTextColor); + + posY += textDrop; + } + else + { + posY += legend.textHeightMax * 3.0; + drawLabel(context, x: posX, y: posY - legend.textHeightMax * 2.0, label: legend.getLabel(i)!, font: labelFont, textColor: labelTextColor); + } + + // make a step down + posY += legend.yEntrySpace; + stack = 0.0; + } + else + { + stack += formSize + stackSpace; + wasStacked = true; + } + } + break; + case .RightOfChartCenter: + var posX = viewPortHandler.chartWidth - legend.textWidthMax - xoffset; + var posY = viewPortHandler.chartHeight / 2.0 - legend.neededHeight / 2.0; + + for (var i = 0; i < labels.count; i++) + { + drawForm(context, x: posX + stack, y: posY, colorIndex: i, legend: legend); + + if (labels[i] != nil) + { + if (!wasStacked) + { + var x = posX; + + if (legend.colors[i] != UIColor.clearColor()) + { + x += formTextSpaceAndForm; + } + + drawLabel(context, x: x, y: posY - legend.textHeightMax / 2.0, label: legend.getLabel(i)!, font: labelFont, textColor: labelTextColor); + + posY += textDrop; + } + else + { + posY += legend.textHeightMax * 3.0; + drawLabel(context, x: posX, y: posY - legend.textHeightMax * 2.0, label: legend.getLabel(i)!, font: labelFont, textColor: labelTextColor); + } + + // make a step down + posY += legend.yEntrySpace; + stack = 0.0; + } + else + { + stack += formSize + stackSpace; + wasStacked = true; + } + } + + break; + case .BelowChartCenter: + + var posX = viewPortHandler.chartWidth / 2.0 - legend.neededWidth / 2.0; + var posY = viewPortHandler.chartHeight - yoffset; + + for (var i = 0; i < labels.count; i++) + { + drawForm(context, x: posX, y: posY - legend.textHeightMax / 2.0, colorIndex: i, legend: legend); + + // grouped forms have null labels + if (labels[i] != nil) + { + // make a step to the left + if (legend.colors[i] != -2) + { + posX += formTextSpaceAndForm; + } + + drawLabel(context, x: posX, y: posY - legend.textHeightMax, label: legend.getLabel(i)!, font: labelFont, textColor: labelTextColor); + posX += (labels[i] as NSString!).sizeWithAttributes([NSFontAttributeName: labelFont]).width + legend.xEntrySpace; + } + else + { + posX += formSize + stackSpace; + } + } + + break; + case .PiechartCenter: + + var posX = viewPortHandler.chartWidth / 2.0 - legend.textWidthMax / 2.0; + var posY = viewPortHandler.chartHeight / 2.0 - legend.neededHeight / 2.0; + + for (var i = 0; i < labels.count; i++) + { + drawForm(context, x: posX + stack, y: posY, colorIndex: i, legend: legend); + + if (labels[i] != nil) + { + if (!wasStacked) + { + var x = posX; + + if (legend.colors[i] != UIColor.clearColor()) + { + x += formTextSpaceAndForm; + } + + drawLabel(context, x: x, y: posY - legend.textHeightMax / 2.0, label: legend.getLabel(i)!, font: labelFont, textColor: labelTextColor); + + posY += textDrop; + } + else + { + posY += legend.textHeightMax * 3.0; + drawLabel(context, x: posX, y: posY - legend.textHeightMax * 2.0, label: legend.getLabel(i)!, font: labelFont, textColor: labelTextColor); + } + + // make a step down + posY += legend.yEntrySpace; + stack = 0.0; + } + else + { + stack += formSize + stackSpace; + wasStacked = true; + } + } + + break; + case .RightOfChartInside: + var posX = viewPortHandler.chartWidth - legend.textWidthMax - xoffset; + var posY = viewPortHandler.contentTop + yoffset; + + for (var i = 0; i < labels.count; i++) + { + drawForm(context, x: posX + stack, y: posY, colorIndex: i, legend: legend); + + if (labels[i] != nil) + { + if (!wasStacked) + { + var x = posX; + + if (legend.colors[i] != UIColor.clearColor()) + { + x += formTextSpaceAndForm; + } + + drawLabel(context, x: x, y: posY - legend.textHeightMax / 2.0, label: legend.getLabel(i)!, font: labelFont, textColor: labelTextColor); + + posY += textDrop; + } + else + { + posY += legend.textHeightMax * 3.0; + drawLabel(context, x: posX, y: posY - legend.textHeightMax * 2.0, label: legend.getLabel(i)!, font: labelFont, textColor: labelTextColor); + } + + // make a step down + posY += legend.yEntrySpace; + stack = 0.0; + } + else + { + stack += formSize + stackSpace; + wasStacked = true; + } + } + break; + } + } + + /// Draws the Legend-form at the given position with the color at the given index. + internal func drawForm(context: CGContext, x: CGFloat, y: CGFloat, colorIndex: Int, legend: ChartLegend) + { + var formColor = legend.colors[colorIndex]; + + if (formColor === nil || formColor == UIColor.clearColor()) + { + return; + } + + var formsize = legend.formSize; + + CGContextSaveGState(context); + + switch (legend.form) + { + case .Circle: + CGContextSetFillColorWithColor(context, formColor!.CGColor); + CGContextFillEllipseInRect(context, CGRect(x: x, y: y - formsize / 2.0, width: formsize, height: formsize)); + break; + case .Square: + CGContextSetFillColorWithColor(context, formColor!.CGColor); + CGContextFillRect(context, CGRect(x: x, y: y - formsize / 2.0, width: formsize, height: formsize)); + break; + case .Line: + + CGContextSetLineWidth(context, legend.formLineWidth); + CGContextSetStrokeColorWithColor(context, formColor!.CGColor); + + var lineSegments = UnsafeMutablePointer.alloc(2) + lineSegments[0] = CGPoint(x: x, y: y) + lineSegments[1] = CGPoint(x: x + formsize, y: y) + CGContextStrokeLineSegments(context, lineSegments, 2); + lineSegments.dealloc(2); + + break; + } + + CGContextRestoreGState(context); + } + + /// Draws the provided label at the given position. + internal func drawLabel(context: CGContext, x: CGFloat, y: CGFloat, label: String, font: UIFont, textColor: UIColor) + { + ChartUtils.drawText(context: context, text: label, point: CGPoint(x: x, y: y), align: .Left, attributes: [NSFontAttributeName: font, NSForegroundColorAttributeName: textColor]); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartRendererBase.swift b/Charts/Classes/Renderers/ChartRendererBase.swift new file mode 100644 index 0000000000..3d56ccc165 --- /dev/null +++ b/Charts/Classes/Renderers/ChartRendererBase.swift @@ -0,0 +1,71 @@ +// +// ChartRendererBase.swift +// Charts +// +// Created by Daniel Cohen Gindi on 3/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartRendererBase: NSObject +{ + /// the component that handles the drawing area of the chart and it's offsets + public var viewPortHandler: ChartViewPortHandler!; + internal var _minX: Int = 0; + internal var _maxX: Int = 0; + + public override init() + { + super.init(); + } + + public init(viewPortHandler: ChartViewPortHandler) + { + super.init(); + self.viewPortHandler = viewPortHandler; + } + + /// Returns true if the specified value fits in between the provided min and max bounds, false if not. + internal func fitsBounds(val: Float, min: Float, max: Float) -> Bool + { + if (val < min || val > max) + { + return false; + } + else + { + return true; + } + } + + /// Calculates the minimum and maximum x-value the chart can currently display (with the given zoom level). + internal func calcXBounds(trans: ChartTransformer!) + { + var minx = trans.getValueByTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: 0.0)).x; + var maxx = trans.getValueByTouchPoint(CGPoint(x: viewPortHandler.contentRight, y: 0.0)).x; + + if (isnan(minx)) + { + minx = 0; + } + if (isnan(maxx)) + { + maxx = 0; + } + + if (!isinf(minx)) + { + _minX = Int(minx); + } + if (!isinf(maxx)) + { + _maxX = Int(ceil(maxx)); + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartXAxisRenderer.swift b/Charts/Classes/Renderers/ChartXAxisRenderer.swift new file mode 100644 index 0000000000..e92dbd5e77 --- /dev/null +++ b/Charts/Classes/Renderers/ChartXAxisRenderer.swift @@ -0,0 +1,233 @@ +// +// ChartXAxisRenderer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 3/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartXAxisRenderer: ChartAxisRendererBase +{ + internal var _xAxis: ChartXAxis!; + + public init(viewPortHandler: ChartViewPortHandler, xAxis: ChartXAxis, transformer: ChartTransformer!) + { + super.init(viewPortHandler: viewPortHandler, transformer: transformer); + + _xAxis = xAxis; + } + + public func computeAxis(#xValAverageLength: Float, xValues: [String]) + { + var a = ""; + + var max = Int(round(xValAverageLength + Float(_xAxis.spaceBetweenLabels))); + + for (var i = 0; i < max; i++) + { + a += "h"; + } + + var widthText = a as NSString; + var heightText = "Q" as NSString; + + _xAxis.labelWidth = widthText.sizeWithAttributes([NSFontAttributeName: _xAxis.labelFont]).width; + _xAxis.labelHeight = _xAxis.labelFont.lineHeight; + _xAxis.values = xValues; + } + + public override func renderAxisLabels(#context: CGContext) + { + if (!_xAxis.isEnabled || !_xAxis.isDrawLabelsEnabled) + { + return; + } + + var yoffset = CGFloat(4.0); + + if (_xAxis.labelPosition == .Top) + { + drawLabels(context: context, pos: viewPortHandler.offsetTop - _xAxis.labelHeight - yoffset); + } + else if (_xAxis.labelPosition == .Bottom) + { + drawLabels(context: context, pos: viewPortHandler.contentBottom + yoffset * 1.5); + } + else if (_xAxis.labelPosition == .BottomInside) + { + drawLabels(context: context, pos: viewPortHandler.contentBottom - _xAxis.labelHeight - yoffset); + } + else if (_xAxis.labelPosition == .TopInside) + { + drawLabels(context: context, pos: viewPortHandler.offsetTop + yoffset); + } + else + { // BOTH SIDED + drawLabels(context: context, pos: viewPortHandler.offsetTop - _xAxis.labelHeight - yoffset); + drawLabels(context: context, pos: viewPortHandler.contentBottom + yoffset * 1.6); + } + } + + internal override func renderAxisLine(#context: CGContext) + { + if (!_xAxis.isEnabled || !_xAxis.isDrawAxisLineEnabled) + { + return; + } + + CGContextSaveGState(context); + + CGContextSetStrokeColorWithColor(context, _xAxis.axisLineColor.CGColor); + CGContextSetLineWidth(context, _xAxis.axisLineWidth); + if (_xAxis.axisLineDashLengths != nil) + { + CGContextSetLineDash(context, _xAxis.axisLineDashPhase, _xAxis.axisLineDashLengths, _xAxis.axisLineDashLengths.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var lineSegments = UnsafeMutablePointer.alloc(2) + + if (_xAxis.labelPosition == .Top + || _xAxis.labelPosition == .TopInside + || _xAxis.labelPosition == .BothSided) + { + lineSegments[0].x = viewPortHandler.contentLeft; + lineSegments[0].y = viewPortHandler.contentTop; + lineSegments[1].x = viewPortHandler.contentRight; + lineSegments[1].y = viewPortHandler.contentTop; + CGContextStrokeLineSegments(context, lineSegments, 2); + } + + if (_xAxis.labelPosition == .Bottom + || _xAxis.labelPosition == .BottomInside + || _xAxis.labelPosition == .BothSided) + { + lineSegments[0].x = viewPortHandler.contentLeft; + lineSegments[0].y = viewPortHandler.contentBottom; + lineSegments[1].x = viewPortHandler.contentRight; + lineSegments[1].y = viewPortHandler.contentBottom; + CGContextStrokeLineSegments(context, lineSegments, 2); + } + + lineSegments.dealloc(2); + + CGContextRestoreGState(context); + } + + /// draws the x-labels on the specified y-position + internal func drawLabels(#context: CGContext, pos: CGFloat) + { + var labelFont = _xAxis.labelFont; + var labelTextColor = _xAxis.labelTextColor; + + var valueToPixelMatrix = transformer.valueToPixelMatrix; + + var position = CGPoint(x: 0.0, y: 0.0); + + var maxx = self._maxX; + var minx = self._minX; + + if (maxx >= _xAxis.values.count) + { + maxx = _xAxis.values.count - 1; + } + if (minx < 0) + { + minx = 0; + } + + for (var i = minx; i <= maxx; i += _xAxis.axisLabelModulus) + { + position.x = CGFloat(i); + position.y = 0.0; + position = CGPointApplyAffineTransform(position, valueToPixelMatrix); + + if (viewPortHandler.isInBoundsX(position.x)) + { + var label = _xAxis.values[i]; + var labelns = label as NSString; + + if (_xAxis.isAvoidFirstLastClippingEnabled) + { + // avoid clipping of the last + if (i == _xAxis.values.count - 1 && _xAxis.values.count > 1) + { + + var width = labelns.sizeWithAttributes([NSFontAttributeName: _xAxis.labelFont]).width; + + if (width > viewPortHandler.offsetRight * 2.0 + && position.x + width > viewPortHandler.chartWidth) + { + position.x -= width / 2.0; + } + } + else if (i == 0) + { // avoid clipping of the first + var width = labelns.sizeWithAttributes([NSFontAttributeName: _xAxis.labelFont]).width; + position.x += width / 2.0; + } + } + + ChartUtils.drawText(context: context, text: label, point: CGPoint(x: position.x, y: pos), align: .Center, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor]); + } + } + } + + public override func renderGridLines(#context: CGContext) + { + if (!_xAxis.isDrawGridLinesEnabled || !_xAxis.isEnabled) + { + return; + } + + CGContextSaveGState(context); + + CGContextSetStrokeColorWithColor(context, _xAxis.gridColor.CGColor); + CGContextSetLineWidth(context, _xAxis.gridLineWidth); + if (_xAxis.gridLineDashLengths != nil) + { + CGContextSetLineDash(context, _xAxis.gridLineDashPhase, _xAxis.gridLineDashLengths, _xAxis.gridLineDashLengths.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var valueToPixelMatrix = transformer.valueToPixelMatrix; + + var position = CGPoint(x: 0.0, y: 0.0); + + var lineSegments = UnsafeMutablePointer.alloc(2) + + for (var i = _minX; i <= _maxX; i += _xAxis.axisLabelModulus) + { + position.x = CGFloat(i); + position.y = 0.0; + position = CGPointApplyAffineTransform(position, valueToPixelMatrix); + + if (position.x >= viewPortHandler.offsetLeft + && position.x <= viewPortHandler.chartWidth) + { + lineSegments[0].x = position.x; + lineSegments[0].y = viewPortHandler.contentTop; + lineSegments[1].x = position.x; + lineSegments[1].y = viewPortHandler.contentBottom; + CGContextStrokeLineSegments(context, lineSegments, 2); + } + } + + lineSegments.dealloc(2); + + CGContextRestoreGState(context); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift b/Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift new file mode 100644 index 0000000000..67ee5173f2 --- /dev/null +++ b/Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift @@ -0,0 +1,145 @@ +// +// ChartXAxisRendererBarChart.swift +// Charts +// +// Created by Daniel Cohen Gindi on 3/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartXAxisRendererBarChart: ChartXAxisRenderer +{ + internal weak var _chart: BarChartView!; + + public init(viewPortHandler: ChartViewPortHandler, xAxis: ChartXAxis, transformer: ChartTransformer!, chart: BarChartView) + { + super.init(viewPortHandler: viewPortHandler, xAxis: xAxis, transformer: transformer); + + self._chart = chart; + } + + /// draws the x-labels on the specified y-position + internal override func drawLabels(#context: CGContext, pos: CGFloat) + { + if (_chart.data === nil) + { + return; + } + + var labelFont = _xAxis.labelFont; + var labelTextColor = _xAxis.labelTextColor; + + var barData = _chart.data as! BarChartData; + var step = barData.dataSetCount; + + var trans = transformer.valueToPixelMatrix; + + var position = CGPoint(x: 0.0, y: 0.0); + + var div = CGFloat(step) + (step > 1 ? barData.groupSpace : 0.0); + var min = Int(CGFloat(_minX) / div); + var max = Int(CGFloat(_maxX) / div); + + for (var i = min; i <= max; i += _xAxis.axisLabelModulus) + { + position.x = CGFloat(i * step) + CGFloat(i) * barData.groupSpace + barData.groupSpace / 2.0; + position.y = 0.0; + + // consider groups (center label for each group) + if (step > 1) + { + position.x += (CGFloat(step) - 1.0) / 2.0; + } + + position = CGPointApplyAffineTransform(position, trans); + + if (viewPortHandler.isInBoundsX(position.x) && i >= 0 && i < _xAxis.values.count) + { + var label = _xAxis.values[i]; + var labelns = label as NSString; + + if (_xAxis.isAvoidFirstLastClippingEnabled) + { + // avoid clipping of the last + if (i == _xAxis.values.count - 1) + { + var width = label.sizeWithAttributes([NSFontAttributeName: _xAxis.labelFont]).width; + + if (width > viewPortHandler.offsetRight * 2.0 + && position.x + width > viewPortHandler.chartWidth) + { + position.x -= width / 2.0; + } + } + else if (i == 0) + { // avoid clipping of the first + var width = label.sizeWithAttributes([NSFontAttributeName: _xAxis.labelFont]).width; + position.x += width / 2.0; + } + } + + ChartUtils.drawText(context: context, text: label, point: CGPoint(x: position.x, y: pos), align: .Center, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor]); + } + } + } + + public override func renderGridLines(#context: CGContext) + { + if (!_xAxis.isDrawGridLinesEnabled || !_xAxis.isEnabled) + { + return; + } + + var barData = _chart.data as! BarChartData; + var step = barData.dataSetCount; + + CGContextSaveGState(context); + + CGContextSetStrokeColorWithColor(context, _xAxis.gridColor.CGColor); + CGContextSetLineWidth(context, _xAxis.gridLineWidth); + if (_xAxis.gridLineDashLengths != nil) + { + CGContextSetLineDash(context, _xAxis.gridLineDashPhase, _xAxis.gridLineDashLengths, _xAxis.gridLineDashLengths.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var trans = transformer.valueToPixelMatrix; + + var position = CGPoint(x: 0.0, y: 0.0); + + var lineSegments = UnsafeMutablePointer.alloc(2) + + var div = CGFloat(step) + (step > 1 ? barData.groupSpace : 0.0); + var min = Int(CGFloat(_minX) / div); + var max = Int(CGFloat(_maxX) / div); + + for (var i = min; i <= max; i += _xAxis.axisLabelModulus) + { + position.x = CGFloat(i * step) + CGFloat(i) * barData.groupSpace - 0.5; + position.y = 0.0; + position = CGPointApplyAffineTransform(position, trans); + + if (viewPortHandler.isInBoundsX(position.x)) + { + lineSegments[0].x = position.x; + lineSegments[0].y = viewPortHandler.contentTop; + lineSegments[1].x = position.x; + lineSegments[1].y = viewPortHandler.contentBottom; + CGContextStrokeLineSegments(context, lineSegments, 2); + } + } + + lineSegments.dealloc(2); + + CGContextRestoreGState(context); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift b/Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift new file mode 100644 index 0000000000..d7e56f3b44 --- /dev/null +++ b/Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift @@ -0,0 +1,196 @@ +// +// ChartXAxisRendererHorizontalBarChart.swift +// Charts +// +// Created by Daniel Cohen Gindi on 3/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRendererBarChart +{ + public override init(viewPortHandler: ChartViewPortHandler, xAxis: ChartXAxis, transformer: ChartTransformer!, chart: BarChartView) + { + super.init(viewPortHandler: viewPortHandler, xAxis: xAxis, transformer: transformer, chart: chart); + } + + public override func computeAxis(#xValAverageLength: Float, xValues: [String]) + { + _xAxis.values = xValues; + + var longest = _xAxis.getLongestLabel() as NSString; + var longestSize = longest.sizeWithAttributes([NSFontAttributeName: _xAxis.labelFont]); + _xAxis.labelWidth = floor(longestSize.width + _xAxis.xOffset * 3.5); + _xAxis.labelHeight = longestSize.height; + } + + public override func renderAxisLabels(#context: CGContext) + { + if (!_xAxis.isEnabled || !_xAxis.isDrawLabelsEnabled || _chart.data === nil) + { + return; + } + + var xoffset = _xAxis.xOffset; + + if (_xAxis.labelPosition == .Top) + { + drawLabels(context: context, pos: viewPortHandler.contentRight + xoffset, align: .Left); + } + else if (_xAxis.labelPosition == .Bottom) + { + drawLabels(context: context, pos: viewPortHandler.contentLeft - xoffset, align: .Right); + } + else if (_xAxis.labelPosition == .BottomInside) + { + drawLabels(context: context, pos: viewPortHandler.contentLeft + xoffset, align: .Left); + } + else if (_xAxis.labelPosition == .TopInside) + { + drawLabels(context: context, pos: viewPortHandler.contentRight - xoffset, align: .Right); + } + else + { // BOTH SIDED + drawLabels(context: context, pos: viewPortHandler.contentLeft, align: .Left); + drawLabels(context: context, pos: viewPortHandler.contentRight, align: .Left); + } + } + + /// draws the x-labels on the specified y-position + internal func drawLabels(#context: CGContext, pos: CGFloat, align: NSTextAlignment) + { + var labelFont = _xAxis.labelFont; + var labelTextColor = _xAxis.labelTextColor; + + // pre allocate to save performance (dont allocate in loop) + var position = CGPoint(x: 0.0, y: 0.0); + + var bd = _chart.data as! BarChartData; + var step = bd.dataSetCount; + + for (var i = 0; i < _xAxis.values.count; i += _xAxis.axisLabelModulus) + { + position.x = 0.0; + position.y = CGFloat(i * step) + CGFloat(i) * bd.groupSpace + bd.groupSpace / 2.0; + + // consider groups (center label for each group) + if (step > 1) + { + position.y += (CGFloat(step) - 1.0) / 2.0; + } + + transformer.pointValueToPixel(&position); + + if (viewPortHandler.isInBoundsY(position.y)) + { + var label = _xAxis.values[i]; + + ChartUtils.drawText(context: context, text: label, point: CGPoint(x: pos, y: position.y - _xAxis.labelHeight / 2.0), align: align, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor]); + } + } + } + + public override func renderGridLines(#context: CGContext) + { + if (!_xAxis.isEnabled || !_xAxis.isDrawGridLinesEnabled || _chart.data === nil) + { + return; + } + + CGContextSaveGState(context); + + CGContextSetStrokeColorWithColor(context, _xAxis.gridColor.CGColor); + CGContextSetLineWidth(context, _xAxis.gridLineWidth); + if (_xAxis.gridLineDashLengths != nil) + { + CGContextSetLineDash(context, _xAxis.gridLineDashPhase, _xAxis.gridLineDashLengths, _xAxis.gridLineDashLengths.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var lineSegments = UnsafeMutablePointer.alloc(2) + + var position = CGPoint(x: 0.0, y: 0.0); + + var bd = _chart.data as! BarChartData; + + // take into consideration that multiple DataSets increase _deltaX + var step = bd.dataSetCount; + + for (var i = 0; i < _xAxis.values.count; i += _xAxis.axisLabelModulus) + { + position.x = 0.0; + position.y = CGFloat(i * step) + CGFloat(i) * bd.groupSpace - 0.5; + + transformer.pointValueToPixel(&position); + + if (viewPortHandler.isInBoundsY(position.y)) + { + lineSegments[0].x = viewPortHandler.contentLeft; + lineSegments[0].y = position.y; + lineSegments[1].x = viewPortHandler.contentRight; + lineSegments[1].y = position.y; + CGContextStrokeLineSegments(context, lineSegments, 2); + } + } + + lineSegments.dealloc(2); + + CGContextRestoreGState(context); + } + + internal override func renderAxisLine(#context: CGContext) + { + if (!_xAxis.isEnabled || !_xAxis.isDrawAxisLineEnabled) + { + return; + } + + CGContextSaveGState(context); + + CGContextSetStrokeColorWithColor(context, _xAxis.axisLineColor.CGColor); + CGContextSetLineWidth(context, _xAxis.axisLineWidth); + if (_xAxis.axisLineDashLengths != nil) + { + CGContextSetLineDash(context, _xAxis.axisLineDashPhase, _xAxis.axisLineDashLengths, _xAxis.axisLineDashLengths.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var lineSegments = UnsafeMutablePointer.alloc(2) + + if (_xAxis.labelPosition == .Top + || _xAxis.labelPosition == .TopInside + || _xAxis.labelPosition == .BothSided) + { + lineSegments[0].x = viewPortHandler.contentRight; + lineSegments[0].y = viewPortHandler.contentTop; + lineSegments[1].x = viewPortHandler.contentRight; + lineSegments[1].y = viewPortHandler.contentBottom; + CGContextStrokeLineSegments(context, lineSegments, 2); + } + + if (_xAxis.labelPosition == .Bottom + || _xAxis.labelPosition == .BottomInside + || _xAxis.labelPosition == .BothSided) + { + lineSegments[0].x = viewPortHandler.contentLeft; + lineSegments[0].y = viewPortHandler.contentTop; + lineSegments[1].x = viewPortHandler.contentLeft; + lineSegments[1].y = viewPortHandler.contentBottom; + CGContextStrokeLineSegments(context, lineSegments, 2); + } + + CGContextRestoreGState(context); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift b/Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift new file mode 100644 index 0000000000..3e94a0adeb --- /dev/null +++ b/Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift @@ -0,0 +1,55 @@ +// +// ChartXAxisRendererRadarChart.swift +// Charts +// +// Created by Daniel Cohen Gindi on 3/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartXAxisRendererRadarChart: ChartXAxisRenderer +{ + private weak var _chart: RadarChartView!; + + public init(viewPortHandler: ChartViewPortHandler, xAxis: ChartXAxis, chart: RadarChartView) + { + super.init(viewPortHandler: viewPortHandler, xAxis: xAxis, transformer: nil); + + _chart = chart; + } + + public override func renderAxisLabels(#context: CGContext) + { + if (!_xAxis.isEnabled || !_xAxis.isDrawLabelsEnabled) + { + return; + } + + var labelFont = _xAxis.labelFont; + var labelTextColor = _xAxis.labelTextColor; + + var sliceangle = _chart.sliceAngle; + + // calculate the factor that is needed for transforming the value to pixels + var factor = _chart.factor; + + var center = _chart.centerOffsets; + + for (var i = 0, count = _xAxis.values.count; i < count; i++) + { + var text = _xAxis.values[i]; + + var angle = (sliceangle * CGFloat(i) + _chart.rotationAngle) % 360.0; + + var p = ChartUtils.getPosition(center: center, dist: CGFloat(_chart.yRange) * factor + _xAxis.labelWidth / 2.0, angle: angle); + + ChartUtils.drawText(context: context, text: text, point: CGPoint(x: p.x, y: p.y - _xAxis.labelHeight / 2.0), align: .Center, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor]); + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartYAxisRenderer.swift b/Charts/Classes/Renderers/ChartYAxisRenderer.swift new file mode 100644 index 0000000000..b0e469b1ff --- /dev/null +++ b/Charts/Classes/Renderers/ChartYAxisRenderer.swift @@ -0,0 +1,355 @@ +// +// ChartYAxisRenderer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 3/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartYAxisRenderer: ChartAxisRendererBase +{ + internal var _yAxis: ChartYAxis! + + public init(viewPortHandler: ChartViewPortHandler, yAxis: ChartYAxis, transformer: ChartTransformer!) + { + super.init(viewPortHandler: viewPortHandler, transformer: transformer); + + _yAxis = yAxis; + } + + /// Computes the axis values. + public func computeAxis(var #yMin: Float, var yMax: Float) + { + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if (viewPortHandler.contentWidth > 10.0 && !viewPortHandler.isFullyZoomedOutY) + { + var p1 = transformer.getValueByTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)); + var p2 = transformer.getValueByTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom)); + + if (!_yAxis.isInverted) + { + yMin = Float(p2.y); + yMax = Float(p1.y); + } + else + { + if (!_yAxis.isStartAtZeroEnabled) + { + yMin = Float(min(p1.y, p2.y)); + } + else + { + yMin = 0.0; + } + yMax = Float(max(p1.y, p2.y)); + } + } + + computeAxisValues(min: yMin, max: yMax); + } + + /// Sets up the y-axis labels. Computes the desired number of labels between + /// the two given extremes. Unlike the papareXLabels() method, this method + /// needs to be called upon every refresh of the view. + internal func computeAxisValues(#min: Float, max: Float) + { + var yMin = min; + var yMax = max; + + var labelCount = _yAxis.labelCount; + var range = abs(yMax - yMin); + + if (labelCount == 0 || range <= 0) + { + _yAxis.entries = [Float](); + return; + } + + var rawInterval = range / Float(labelCount); + var interval = ChartUtils.roundToNextSignificant(number: Double(rawInterval)); + var intervalMagnitude = pow(10.0, round(log10(interval))); + var intervalSigDigit = (interval / intervalMagnitude); + if (intervalSigDigit > 5) + { + // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 + interval = floor(10.0 * intervalMagnitude); + } + + // if the labels should only show min and max + if (_yAxis.isShowOnlyMinMaxEnabled) + { + _yAxis.entries = [yMin, yMax]; + } + else + { + var first = ceil(Double(yMin) / interval) * interval; + var last = ChartUtils.nextUp(floor(Double(yMax) / interval) * interval); + + var f: Double; + var i: Int; + var n = 0; + for (f = first; f <= last; f += interval) + { + ++n; + } + + if (_yAxis.entries.count < n) + { + // Ensure stops contains at least numStops elements. + _yAxis.entries = [Float](count: n, repeatedValue: 0.0); + } + else if (_yAxis.entries.count > n) + { + _yAxis.entries.removeRange(n..<_yAxis.entries.count); + } + + for (f = first, i = 0; i < n; f += interval, ++i) + { + _yAxis.entries[i] = Float(f); + } + } + } + + /// draws the y-axis labels to the screen + public override func renderAxisLabels(#context: CGContext) + { + if (!_yAxis.isEnabled || !_yAxis.isDrawLabelsEnabled) + { + return; + } + + var xoffset = _yAxis.xOffset; + var yoffset = _yAxis.labelFont.lineHeight / 2.5; + + var dependency = _yAxis.axisDependency; + var labelPosition = _yAxis.labelPosition; + + var xPos = CGFloat(0.0); + + var textAlign: NSTextAlignment; + + if (dependency == .Left) + { + if (labelPosition == .OutsideChart) + { + textAlign = .Right; + xPos = viewPortHandler.offsetLeft - xoffset; + } + else + { + textAlign = .Left; + xPos = viewPortHandler.offsetLeft + xoffset; + } + + } + else + { + if (labelPosition == .OutsideChart) + { + textAlign = .Left; + xPos = viewPortHandler.contentRight + xoffset; + } + else + { + textAlign = .Right; + xPos = viewPortHandler.contentRight - xoffset; + } + } + + drawYLabels(context: context, fixedPosition: xPos, offset: yoffset - _yAxis.labelFont.lineHeight, textAlign: textAlign); + } + + internal override func renderAxisLine(#context: CGContext) + { + if (!_yAxis.isEnabled || !_yAxis.drawAxisLineEnabled) + { + return; + } + + CGContextSaveGState(context); + + CGContextSetStrokeColorWithColor(context, _yAxis.axisLineColor.CGColor); + CGContextSetLineWidth(context, _yAxis.axisLineWidth); + if (_yAxis.axisLineDashLengths != nil) + { + CGContextSetLineDash(context, _yAxis.axisLineDashPhase, _yAxis.axisLineDashLengths, _yAxis.axisLineDashLengths.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var lineSegments = UnsafeMutablePointer.alloc(2) + + if (_yAxis.axisDependency == .Left) + { + lineSegments[0].x = viewPortHandler.contentLeft; + lineSegments[0].y = viewPortHandler.contentTop; + lineSegments[1].x = viewPortHandler.contentLeft; + lineSegments[1].y = viewPortHandler.contentBottom; + CGContextStrokeLineSegments(context, lineSegments, 2); + } + else + { + lineSegments[0].x = viewPortHandler.contentRight; + lineSegments[0].y = viewPortHandler.contentTop; + lineSegments[1].x = viewPortHandler.contentRight; + lineSegments[1].y = viewPortHandler.contentBottom; + CGContextStrokeLineSegments(context, lineSegments, 2); + } + + lineSegments.dealloc(2); + + CGContextRestoreGState(context); + } + + /// draws the y-labels on the specified x-position + internal func drawYLabels(#context: CGContext, fixedPosition: CGFloat, offset: CGFloat, textAlign: NSTextAlignment) + { + var labelFont = _yAxis.labelFont; + var labelTextColor = _yAxis.labelTextColor; + + var valueToPixelMatrix = transformer.valueToPixelMatrix; + + var pt = CGPoint(); + + for (var i = 0; i < _yAxis.entryCount; i++) + { + var text = _yAxis.getFormattedLabel(i); + + if (!_yAxis.isDrawTopYLabelEntryEnabled && i >= _yAxis.entryCount - 1) + { + break; + } + + pt.x = 0; + pt.y = CGFloat(_yAxis.entries[i]); + pt = CGPointApplyAffineTransform(pt, valueToPixelMatrix); + + pt.x = fixedPosition; + pt.y += offset; + + ChartUtils.drawText(context: context, text: text, point: pt, align: textAlign, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor]); + } + } + + private var _gridLineBuffer = [CGPoint](count: 2, repeatedValue: CGPoint()); + + public override func renderGridLines(#context: CGContext) + { + if (!_yAxis.isDrawGridLinesEnabled || !_yAxis.isEnabled) + { + return; + } + + CGContextSaveGState(context); + + CGContextSetStrokeColorWithColor(context, _yAxis.gridColor.CGColor); + CGContextSetLineWidth(context, _yAxis.gridLineWidth); + if (_yAxis.gridLineDashLengths != nil) + { + CGContextSetLineDash(context, _yAxis.gridLineDashPhase, _yAxis.gridLineDashLengths, _yAxis.gridLineDashLengths.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var valueToPixelMatrix = transformer.valueToPixelMatrix; + + var position = CGPoint(x: 0.0, y: 0.0); + + // draw the horizontal grid + for (var i = 0, count = _yAxis.entryCount; i < count; i++) + { + position.x = 0.0; + position.y = CGFloat(_yAxis.entries[i]); + position = CGPointApplyAffineTransform(position, valueToPixelMatrix) + + _gridLineBuffer[0].x = viewPortHandler.contentLeft; + _gridLineBuffer[0].y = position.y; + _gridLineBuffer[1].x = viewPortHandler.contentRight; + _gridLineBuffer[1].y = position.y; + CGContextStrokeLineSegments(context, _gridLineBuffer, 2); + } + + CGContextRestoreGState(context); + } + + /// Draws the LimitLines associated with this axis to the screen. + public func renderLimitLines(#context: CGContext) + { + var limitLines = _yAxis.limitLines; + + if (limitLines.count == 0) + { + return; + } + + CGContextSaveGState(context); + + var trans = transformer.valueToPixelMatrix; + + var position = CGPoint(x: 0.0, y: 0.0); + + var lineSegments = UnsafeMutablePointer.alloc(2) + + for (var i = 0; i < limitLines.count; i++) + { + var l = limitLines[i]; + + position.x = 0.0; + position.y = CGFloat(l.limit); + position = CGPointApplyAffineTransform(position, trans); + + lineSegments[0].x = viewPortHandler.contentLeft; + lineSegments[0].y = position.y; + lineSegments[1].x = viewPortHandler.contentRight; + lineSegments[1].y = position.y; + + CGContextSetStrokeColorWithColor(context, l.lineColor.CGColor); + CGContextSetLineWidth(context, l.lineWidth); + if (l.lineDashLengths != nil) + { + CGContextSetLineDash(context, l.lineDashPhase, l.lineDashLengths!, l.lineDashLengths!.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + CGContextStrokeLineSegments(context, lineSegments, 2); + + var label = l.label; + + // if drawing the limit-value label is enabled + if (label.lengthOfBytesUsingEncoding(NSUTF16StringEncoding) > 0) + { + var xOffset = CGFloat(4.0); + var labelLineHeight = l.valueFont.lineHeight; + var yOffset = l.lineWidth + labelLineHeight / 2.0; + + if (l.labelPosition == .Right) + { + ChartUtils.drawText(context: context, text: label, point: CGPoint(x: viewPortHandler.contentRight - xOffset, y: position.y - yOffset - labelLineHeight), align: .Right, attributes: [NSFontAttributeName: l.valueFont, NSForegroundColorAttributeName: l.valueTextColor]); + } + else + { + ChartUtils.drawText(context: context, text: label, point: CGPoint(x: viewPortHandler.contentLeft + xOffset, y: position.y - yOffset - labelLineHeight), align: .Left, attributes: [NSFontAttributeName: l.valueFont, NSForegroundColorAttributeName: l.valueTextColor]); + } + } + } + + lineSegments.dealloc(2); + + CGContextRestoreGState(context); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartYAxisRendererHorizontalBarChart.swift b/Charts/Classes/Renderers/ChartYAxisRendererHorizontalBarChart.swift new file mode 100644 index 0000000000..9822a5f400 --- /dev/null +++ b/Charts/Classes/Renderers/ChartYAxisRendererHorizontalBarChart.swift @@ -0,0 +1,278 @@ +// +// ChartYAxisRendererHorizontalBarChart.swift +// Charts +// +// Created by Daniel Cohen Gindi on 3/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartYAxisRendererHorizontalBarChart: ChartYAxisRenderer +{ + public override init(viewPortHandler: ChartViewPortHandler, yAxis: ChartYAxis, transformer: ChartTransformer!) + { + super.init(viewPortHandler: viewPortHandler, yAxis: yAxis, transformer: transformer); + } + + /// Computes the axis values. + public override func computeAxis(var #yMin: Float, var yMax: Float) + { + // calculate the starting and entry point of the y-labels (depending on zoom / contentrect bounds) + if (viewPortHandler.contentHeight > 10.0 && !viewPortHandler.isFullyZoomedOutX) + { + var p1 = transformer.getValueByTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)); + var p2 = transformer.getValueByTouchPoint(CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentTop)); + + if (!_yAxis.isInverted) + { + yMin = Float(p1.x); + yMax = Float(p2.x); + } + else + { + if (!_yAxis.isStartAtZeroEnabled) + { + yMin = Float(min(p1.x, p2.x)); + } + else + { + yMin = 0.0; + } + yMax = Float(max(p1.x, p2.x)); + } + } + + computeAxisValues(min: yMin, max: yMax); + } + + /// draws the y-axis labels to the screen + public override func renderAxisLabels(#context: CGContext) + { + if (!_yAxis.isEnabled || !_yAxis.isDrawLabelsEnabled) + { + return; + } + + var positions = [CGPoint](); + positions.reserveCapacity(_yAxis.entries.count); + + for (var i = 0; i < _yAxis.entries.count; i++) + { + positions.append(CGPoint(x: CGFloat(_yAxis.entries[i]), y: 0.0)) + } + + transformer.pointValuesToPixel(&positions); + + var lineHeight = _yAxis.labelFont.lineHeight; + var yoffset = lineHeight + _yAxis.yOffset; + + var dependency = _yAxis.axisDependency; + var labelPosition = _yAxis.labelPosition; + + var yPos: CGFloat = 0.0; + + if (dependency == .Left) + { + if (labelPosition == .OutsideChart) + { + yoffset = 3.0; + yPos = viewPortHandler.contentTop; + } + else + { + yoffset = yoffset * -1; + yPos = viewPortHandler.contentTop; + } + } + else + { + if (labelPosition == .OutsideChart) + { + yoffset = yoffset * -1.0; + yPos = viewPortHandler.contentBottom; + } + else + { + yoffset = 4.0; + yPos = viewPortHandler.contentBottom; + } + } + + yPos -= lineHeight; + + drawYLabels(context: context, fixedPosition: yPos, positions: positions, offset: yoffset); + } + + internal override func renderAxisLine(#context: CGContext) + { + if (!_yAxis.isEnabled || !_yAxis.drawAxisLineEnabled) + { + return; + } + + CGContextSaveGState(context); + + CGContextSetStrokeColorWithColor(context, _yAxis.axisLineColor.CGColor); + CGContextSetLineWidth(context, _yAxis.axisLineWidth); + if (_yAxis.axisLineDashLengths != nil) + { + CGContextSetLineDash(context, _yAxis.axisLineDashPhase, _yAxis.axisLineDashLengths, _yAxis.axisLineDashLengths.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var lineSegments = UnsafeMutablePointer.alloc(2) + + if (_yAxis.axisDependency == .Left) + { + lineSegments[0].x = viewPortHandler.contentLeft; + lineSegments[0].y = viewPortHandler.contentTop; + lineSegments[1].x = viewPortHandler.contentRight; + lineSegments[1].y = viewPortHandler.contentTop; + CGContextStrokeLineSegments(context, lineSegments, 2); + } + else + { + lineSegments[0].x = viewPortHandler.contentLeft; + lineSegments[0].y = viewPortHandler.contentBottom; + lineSegments[1].x = viewPortHandler.contentRight; + lineSegments[1].y = viewPortHandler.contentBottom; + CGContextStrokeLineSegments(context, lineSegments, 2); + } + + lineSegments.dealloc(2); + + CGContextRestoreGState(context); + } + + /// draws the y-labels on the specified x-position + internal func drawYLabels(#context: CGContext, fixedPosition: CGFloat, positions: [CGPoint], offset: CGFloat) + { + var labelFont = _yAxis.labelFont; + var labelTextColor = _yAxis.labelTextColor; + + for (var i = 0; i < _yAxis.entryCount; i++) + { + var text = _yAxis.getFormattedLabel(i); + + if (!_yAxis.isDrawTopYLabelEntryEnabled && i >= _yAxis.entryCount - 1) + { + return; + } + + ChartUtils.drawText(context: context, text: text, point: CGPoint(x: positions[i].x, y: fixedPosition - offset), align: .Center, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor]); + } + } + + public override func renderGridLines(#context: CGContext) + { + if (!_yAxis.isEnabled || !_yAxis.isDrawGridLinesEnabled) + { + return; + } + + CGContextSaveGState(context); + + // pre alloc + var position = CGPoint(); + + CGContextSetStrokeColorWithColor(context, _yAxis.gridColor.CGColor); + CGContextSetLineWidth(context, _yAxis.gridLineWidth); + if (_yAxis.gridLineDashLengths != nil) + { + CGContextSetLineDash(context, _yAxis.gridLineDashPhase, _yAxis.gridLineDashLengths, _yAxis.gridLineDashLengths.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + // draw the horizontal grid + for (var i = 0; i < _yAxis.entryCount; i++) + { + position.x = CGFloat(_yAxis.entries[i]); + position.y = 0.0; + transformer.pointValueToPixel(&position); + + CGContextBeginPath(context); + CGContextMoveToPoint(context, position.x, viewPortHandler.contentTop); + CGContextAddLineToPoint(context, position.x, viewPortHandler.contentBottom); + CGContextStrokePath(context); + } + + CGContextRestoreGState(context); + } + + /// Draws the LimitLines associated with this axis to the screen. + public override func renderLimitLines(#context: CGContext) + { + var limitLines = _yAxis.limitLines; + + if (limitLines.count <= 0) + { + return; + } + + var pts = [CGPoint](count: 2, repeatedValue: CGPoint()); + var limitLinePath = CGPathCreateMutable(); + + CGContextSaveGState(context); + + for (var i = 0; i < limitLines.count; i++) + { + var l = limitLines[i]; + + pts[0].x = CGFloat(l.limit); + pts[1].x = CGFloat(l.limit); + + transformer.pointValuesToPixel(&pts); + + pts[0].y = viewPortHandler.contentTop; + pts[1].y = viewPortHandler.contentBottom; + + CGContextSetStrokeColorWithColor(context, l.lineColor.CGColor); + CGContextSetLineWidth(context, l.lineWidth); + if (l.lineDashLengths != nil) + { + CGContextSetLineDash(context, l.lineDashPhase, l.lineDashLengths!, l.lineDashLengths!.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + CGContextStrokeLineSegments(context, pts, 2); + + var label = l.label; + + // if drawing the limit-value label is enabled + if (label.lengthOfBytesUsingEncoding(NSUTF16StringEncoding) > 0) + { + var xOffset = l.lineWidth; + var add: CGFloat = 4.0; + + var valueFont = l.valueFont; + var yOffset = valueFont.lineHeight + add / 2.0; + + if (l.labelPosition == .Right) + { + ChartUtils.drawText(context: context, text: label, point: CGPoint(x: pts[0].x + xOffset - valueFont.lineHeight, y: viewPortHandler.contentBottom - add), align: .Left, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: l.valueTextColor]); + } + else + { + ChartUtils.drawText(context: context, text: label, point: CGPoint(x: pts[0].x + xOffset - valueFont.lineHeight, y: viewPortHandler.contentTop + yOffset), align: .Left, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: l.valueTextColor]); + } + } + } + + CGContextRestoreGState(context); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartYAxisRendererRadarChart.swift b/Charts/Classes/Renderers/ChartYAxisRendererRadarChart.swift new file mode 100644 index 0000000000..ae4ec2f73b --- /dev/null +++ b/Charts/Classes/Renderers/ChartYAxisRendererRadarChart.swift @@ -0,0 +1,184 @@ +// +// ChartYAxisRendererRadarChart.swift +// Charts +// +// Created by Daniel Cohen Gindi on 3/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartYAxisRendererRadarChart: ChartYAxisRenderer +{ + private weak var _chart: RadarChartView!; + + public init(viewPortHandler: ChartViewPortHandler, yAxis: ChartYAxis, chart: RadarChartView) + { + super.init(viewPortHandler: viewPortHandler, yAxis: yAxis, transformer: nil); + + _chart = chart; + } + + public override func computeAxis(#yMin: Float, yMax: Float) + { + computeAxisValues(min: yMin, max: yMax); + } + + internal override func computeAxisValues(min yMin: Float, max yMax: Float) + { + var labelCount = _yAxis.labelCount; + var range = abs(yMax - yMin); + + if (labelCount == 0 || range <= 0) + { + _yAxis.entries = [Float](); + return; + } + + var rawInterval = range / Float(labelCount); + var interval = ChartUtils.roundToNextSignificant(number: Double(rawInterval)); + var intervalMagnitude = pow(10.0, round(log10(interval))); + var intervalSigDigit = Int(interval / intervalMagnitude); + + if (intervalSigDigit > 5) + { + // Use one order of magnitude higher, to avoid intervals like 0.9 or + // 90 + interval = floor(10 * intervalMagnitude); + } + + // if the labels should only show min and max + if (_yAxis.isShowOnlyMinMaxEnabled) + { + _yAxis.entries = [Float](); + _yAxis.entries.append(yMin); + _yAxis.entries.append(yMax); + } + else + { + var first = ceil(Double(yMin) / interval) * interval; + var last = ChartUtils.nextUp(floor(Double(yMax) / interval) * interval); + + var f: Double; + var i: Int; + var n = 0; + for (f = first; f <= last; f += interval) + { + ++n; + } + + if (isnan(_yAxis.customAxisMax)) + { + n += 1; + } + + if (_yAxis.entries.count < n) + { + // Ensure stops contains at least numStops elements. + _yAxis.entries = [Float](count: n, repeatedValue: 0.0); + } + + for (f = first, i = 0; i < n; f += interval, ++i) + { + _yAxis.entries[i] = Float(f); + } + } + + _yAxis.axisMaximum = _yAxis.entries[_yAxis.entryCount - 1]; + _yAxis.axisRange = abs(_yAxis.axisMaximum - _yAxis.axisMinimum); + } + + public override func renderAxisLabels(#context: CGContext) + { + if (!_yAxis.isEnabled || !_yAxis.isDrawLabelsEnabled) + { + return; + } + + var labelFont = _yAxis.labelFont; + var labelTextColor = _yAxis.labelTextColor; + + var center = _chart.centerOffsets; + var factor = _chart.factor; + + var labelCount = _yAxis.entryCount; + + var labelLineHeight = _yAxis.labelFont.lineHeight; + + for (var j = 0; j < labelCount; j++) + { + if (j == labelCount - 1 && _yAxis.isDrawTopYLabelEntryEnabled == false) + { + break; + } + + var r = CGFloat(_yAxis.entries[j] - _yAxis.axisMinimum) * factor; + + var p = ChartUtils.getPosition(center: center, dist: r, angle: _chart.rotationAngle); + + var label = _yAxis.getFormattedLabel(j); + + ChartUtils.drawText(context: context, text: label, point: CGPoint(x: p.x + 10.0, y: p.y - labelLineHeight), align: .Left, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor]); + } + } + + public override func renderLimitLines(#context: CGContext) + { + var limitLines = _yAxis.limitLines; + + if (limitLines.count == 0) + { + return; + } + + var sliceangle = _chart.sliceAngle; + + // calculate the factor that is needed for transforming the value to pixels + var factor = _chart.factor; + + var center = _chart.centerOffsets; + + for (var i = 0; i < limitLines.count; i++) + { + var l = limitLines[i]; + + CGContextSetStrokeColorWithColor(context, l.lineColor.CGColor); + CGContextSetLineWidth(context, l.lineWidth); + if (l.lineDashLengths != nil) + { + CGContextSetLineDash(context, l.lineDashPhase, l.lineDashLengths!, l.lineDashLengths!.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var r = CGFloat(l.limit - _chart.chartYMin) * factor; + + CGContextBeginPath(context); + + for (var j = 0, count = _chart.data!.xValCount; j < count; j++) + { + var p = ChartUtils.getPosition(center: center, dist: r, angle: sliceangle * CGFloat(j) + _chart.rotationAngle); + + if (j == 0) + { + CGContextMoveToPoint(context, p.x, p.y); + } + else + { + CGContextAddLineToPoint(context, p.x, p.y); + } + } + + CGContextClosePath(context); + + CGContextStrokePath(context); + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/CombinedChartRenderer.swift b/Charts/Classes/Renderers/CombinedChartRenderer.swift new file mode 100644 index 0000000000..faa6172eae --- /dev/null +++ b/Charts/Classes/Renderers/CombinedChartRenderer.swift @@ -0,0 +1,341 @@ +// +// CombinedChartRenderer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class CombinedChartRenderer: ChartDataRendererBase, + LineChartRendererDelegate, + BarChartRendererDelegate, + ScatterChartRendererDelegate, + CandleStickChartRendererDelegate +{ + private weak var _chart: CombinedChartView!; + + /// flag that enables or disables the highlighting arrow + public var drawHighlightArrowEnabled = false; + + /// if set to true, all values are drawn above their bars, instead of below their top + public var drawValueAboveBarEnabled = true; + + /// if set to true, all values of a stack are drawn individually, and not just their sum + public var drawValuesForWholeStackEnabled = true; + + /// if set to true, a grey area is darawn behind each bar that indicates the maximum value + public var drawBarShadowEnabled = true; + + internal var _renderers = [ChartDataRendererBase](); + + internal var _drawOrder: [CombinedChartView.DrawOrder] = [.Bar, .Line, .Candle, .Scatter]; + + public init(chart: CombinedChartView, animator: ChartAnimator, viewPortHandler: ChartViewPortHandler) + { + super.init(animator: animator, viewPortHandler: viewPortHandler); + + _chart = chart; + + createRenderers(); + } + + /// Creates the renderers needed for this combined-renderer in the required order. Also takes the DrawOrder into consideration. + internal func createRenderers() + { + _renderers = [ChartDataRendererBase](); + + for order in drawOrder + { + switch (order) + { + case .Bar: + if (_chart.barData !== nil) + { + _renderers.append(BarChartRenderer(delegate: self, animator: _animator, viewPortHandler: viewPortHandler)); + } + break; + case .Line: + if (_chart.lineData !== nil) + { + _renderers.append(LineChartRenderer(delegate: self, animator: _animator, viewPortHandler: viewPortHandler)); + } + break; + case .Candle: + if (_chart.candleData !== nil) + { + _renderers.append(CandleStickChartRenderer(delegate: self, animator: _animator, viewPortHandler: viewPortHandler)); + } + break; + case .Scatter: + if (_chart.scatterData !== nil) + { + _renderers.append(ScatterChartRenderer(delegate: self, animator: _animator, viewPortHandler: viewPortHandler)); + } + break; + } + } + + } + + public override func drawData(#context: CGContext) + { + for renderer in _renderers + { + renderer.drawData(context: context); + } + } + + public override func drawValues(#context: CGContext) + { + for renderer in _renderers + { + renderer.drawValues(context: context); + } + } + + public override func drawExtras(#context: CGContext) + { + for renderer in _renderers + { + renderer.drawExtras(context: context); + } + } + + public override func drawHighlighted(#context: CGContext, indices: [ChartHighlight]) + { + for renderer in _renderers + { + renderer.drawHighlighted(context: context, indices: indices); + } + } + + /// Returns the sub-renderer object at the specified index. + public func getSubRenderer(#index: Int) -> ChartDataRendererBase! + { + if (index >= _renderers.count || index < 0) + { + return nil; + } + else + { + return _renderers[index]; + } + } + + // MARK: - LineChartRendererDelegate + + public func lineChartRendererData(renderer: LineChartRenderer) -> LineChartData! + { + return _chart.lineData; + } + + public func lineChartRenderer(renderer: LineChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer! + { + return _chart.getTransformer(which); + } + + public func lineChartRendererFillFormatter(renderer: LineChartRenderer) -> ChartFillFormatter + { + return _chart.fillFormatter; + } + + public func lineChartDefaultRendererValueFormatter(renderer: LineChartRenderer) -> NSNumberFormatter! + { + return _chart._defaultValueFormatter; + } + + public func lineChartRendererChartYMax(renderer: LineChartRenderer) -> Float + { + return _chart.chartYMax; + } + + public func lineChartRendererChartYMin(renderer: LineChartRenderer) -> Float + { + return _chart.chartYMin; + } + + public func lineChartRendererChartXMax(renderer: LineChartRenderer) -> Float + { + return _chart.chartXMax; + } + + public func lineChartRendererChartXMin(renderer: LineChartRenderer) -> Float + { + return _chart.chartXMin; + } + + public func lineChartRendererMaxVisibleValueCount(renderer: LineChartRenderer) -> Int + { + return _chart.maxVisibleValueCount; + } + + // MARK: - BarChartRendererDelegate + + public func barChartRendererData(renderer: BarChartRenderer) -> BarChartData! + { + return _chart.barData; + } + + public func barChartRenderer(renderer: BarChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer! + { + return _chart.getTransformer(which); + } + + public func barChartRendererMaxVisibleValueCount(renderer: BarChartRenderer) -> Int + { + return _chart.maxVisibleValueCount; + } + + public func barChartDefaultRendererValueFormatter(renderer: BarChartRenderer) -> NSNumberFormatter! + { + return _chart._defaultValueFormatter; + } + + public func barChartRendererChartXMax(renderer: BarChartRenderer) -> Float + { + return _chart.chartXMax; + } + + public func barChartIsDrawHighlightArrowEnabled(renderer: BarChartRenderer) -> Bool + { + return drawHighlightArrowEnabled; + } + + public func barChartIsDrawValueAboveBarEnabled(renderer: BarChartRenderer) -> Bool + { + return drawValueAboveBarEnabled; + } + + public func barChartIsDrawValuesForWholeStackEnabled(renderer: BarChartRenderer) -> Bool + { + return drawValuesForWholeStackEnabled; + } + + public func barChartIsDrawBarShadowEnabled(renderer: BarChartRenderer) -> Bool + { + return drawBarShadowEnabled; + } + + // MARK: - ScatterChartRendererDelegate + + public func scatterChartRendererData(renderer: ScatterChartRenderer) -> ScatterChartData! + { + return _chart.scatterData; + } + + public func scatterChartRenderer(renderer: ScatterChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer! + { + return _chart.getTransformer(which); + } + + public func scatterChartDefaultRendererValueFormatter(renderer: ScatterChartRenderer) -> NSNumberFormatter! + { + return _chart._defaultValueFormatter; + } + + public func scatterChartRendererChartYMax(renderer: ScatterChartRenderer) -> Float + { + return _chart.chartYMax; + } + + public func scatterChartRendererChartYMin(renderer: ScatterChartRenderer) -> Float + { + return _chart.chartYMin; + } + + public func scatterChartRendererChartXMax(renderer: ScatterChartRenderer) -> Float + { + return _chart.chartXMax; + } + + public func scatterChartRendererChartXMin(renderer: ScatterChartRenderer) -> Float + { + return _chart.chartXMin; + } + + public func scatterChartRendererMaxVisibleValueCount(renderer: ScatterChartRenderer) -> Int + { + return _chart.maxVisibleValueCount; + } + + // MARK: - CandleStickChartRendererDelegate + + public func candleStickChartRendererCandleData(renderer: CandleStickChartRenderer) -> CandleChartData! + { + return _chart.candleData; + } + + public func candleStickChartRenderer(renderer: CandleStickChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer! + { + return _chart.getTransformer(which); + } + + public func candleStickChartDefaultRendererValueFormatter(renderer: CandleStickChartRenderer) -> NSNumberFormatter! + { + return _chart._defaultValueFormatter; + } + + public func candleStickChartRendererChartYMax(renderer: CandleStickChartRenderer) -> Float + { + return _chart.chartYMax; + } + + public func candleStickChartRendererChartYMin(renderer: CandleStickChartRenderer) -> Float + { + return _chart.chartYMin; + } + + public func candleStickChartRendererChartXMax(renderer: CandleStickChartRenderer) -> Float + { + return _chart.chartXMax; + } + + public func candleStickChartRendererChartXMin(renderer: CandleStickChartRenderer) -> Float + { + return _chart.chartXMin; + } + + public func candleStickChartRendererMaxVisibleValueCount(renderer: CandleStickChartRenderer) -> Int + { + return _chart.maxVisibleValueCount; + } + + // MARK: Accessors + + /// returns true if drawing the highlighting arrow is enabled, false if not + public var isDrawHighlightArrowEnabled: Bool { return drawHighlightArrowEnabled; } + + /// returns true if drawing values above bars is enabled, false if not + public var isDrawValueAboveBarEnabled: Bool { return drawValueAboveBarEnabled; } + + /// returns true if all values of a stack are drawn, and not just their sum + public var isDrawValuesForWholeStackEnabled: Bool { return drawValuesForWholeStackEnabled; } + + /// returns true if drawing shadows (maxvalue) for each bar is enabled, false if not + public var isDrawBarShadowEnabled: Bool { return drawBarShadowEnabled; } + + /// the order in which the provided data objects should be drawn. + /// The earlier you place them in the provided array, the further they will be in the background. + /// e.g. if you provide [DrawOrder.Bar, DrawOrder.Line], the bars will be drawn behind the lines. + public var drawOrder: [CombinedChartView.DrawOrder] + { + get + { + return _drawOrder; + } + set + { + if (newValue.count > 0) + { + _drawOrder = newValue; + } + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/HorizontalBarChartRenderer.swift b/Charts/Classes/Renderers/HorizontalBarChartRenderer.swift new file mode 100644 index 0000000000..8d88d0dac5 --- /dev/null +++ b/Charts/Classes/Renderers/HorizontalBarChartRenderer.swift @@ -0,0 +1,404 @@ +// +// HorizontalBarChartRenderer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class HorizontalBarChartRenderer: BarChartRenderer +{ + private var xOffset: CGFloat = 0.0; + private var yOffset: CGFloat = 0.0; + + public override init(delegate: BarChartRendererDelegate?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + { + super.init(delegate: delegate, animator: animator, viewPortHandler: viewPortHandler); + } + + internal override func drawDataSet(#context: CGContext, dataSet: BarChartDataSet, index: Int) + { + CGContextSaveGState(context); + + var barData = delegate!.barChartRendererData(self); + + var trans = delegate!.barChartRenderer(self, transformerForAxis: dataSet.axisDependency); + calcXBounds(trans); + + var drawBarShadowEnabled: Bool = delegate!.barChartIsDrawBarShadowEnabled(self); + var dataSetOffset = (barData.dataSetCount - 1); + var groupSpace = barData.groupSpace; + var groupSpaceHalf = groupSpace / 2.0; + var barSpace = dataSet.barSpace; + var barSpaceHalf = barSpace / 2.0; + var containsStacks = dataSet.isStacked; + var entries = dataSet.yVals as! [BarChartDataEntry]; + var barWidth: CGFloat = 0.5; + var phaseY = _animator.phaseY; + var barRect = CGRect(); + var barShadow = CGRect(); + var y: Float; + + // do the drawing + for (var j = 0, count = Int(ceil(CGFloat(dataSet.entryCount) * _animator.phaseX)); j < count; j++) + { + var e = entries[j]; + + // calculate the x-position, depending on datasetcount + var x = CGFloat(e.xIndex + j * dataSetOffset) + CGFloat(index) + + groupSpace * CGFloat(j) + groupSpaceHalf; + var vals = e.values; + + if (!containsStacks || vals == nil) + { + y = e.value; + + var bottom = x - barWidth + barSpaceHalf; + var top = x + barWidth - barSpaceHalf; + var right = y >= 0.0 ? CGFloat(y) : 0; + var left = y <= 0.0 ? CGFloat(y) : 0; + + // multiply the height of the rect with the phase + if (right > 0) + { + right *= phaseY; + } + else + { + left *= phaseY; + } + + barRect.origin.x = left; + barRect.size.width = right - left; + barRect.origin.y = top; + barRect.size.height = bottom - top; + + trans.rectValueToPixel(&barRect); + + if (!viewPortHandler.isInBoundsTop(barRect.origin.y + barRect.size.height)) + { + continue; + } + + if (!viewPortHandler.isInBoundsBottom(barRect.origin.y)) + { + break; + } + + // if drawing the bar shadow is enabled + if (drawBarShadowEnabled) + { + barShadow.origin.x = viewPortHandler.contentLeft; + barShadow.origin.y = barRect.origin.y; + barShadow.size.width = viewPortHandler.contentWidth; + barShadow.size.height = barRect.size.height; + + CGContextSetFillColorWithColor(context, dataSet.barShadowColor.CGColor); + CGContextFillRect(context, barShadow); + } + + // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. + CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor); + CGContextFillRect(context, barRect); + } + else + { + var all = e.value; + + // if drawing the bar shadow is enabled + if (drawBarShadowEnabled) + { + y = e.value; + + var bottom = x - barWidth + barSpaceHalf; + var top = x + barWidth - barSpaceHalf; + var right = y >= 0.0 ? CGFloat(y) : 0.0; + var left = y <= 0.0 ? CGFloat(y) : 0.0; + + // multiply the height of the rect with the phase + if (right > 0) + { + right *= phaseY; + } + else + { + left *= phaseY; + } + + barRect.origin.x = left; + barRect.size.width = right - left; + barRect.origin.y = top; + barRect.size.height = bottom - top; + + trans.rectValueToPixel(&barRect); + + barShadow.origin.x = viewPortHandler.contentLeft; + barShadow.origin.y = barRect.origin.y; + barShadow.size.width = viewPortHandler.contentWidth; + barShadow.size.height = barRect.size.height; + + CGContextSetFillColorWithColor(context, dataSet.barShadowColor.CGColor); + CGContextFillRect(context, barShadow); + } + + // fill the stack + for (var k = 0; k < vals.count; k++) + { + all -= vals[k]; + y = vals[k] + all; + + var bottom = x - barWidth + barSpaceHalf; + var top = x + barWidth - barSpaceHalf; + var right = y >= 0.0 ? CGFloat(y) : 0.0; + var left = y <= 0.0 ? CGFloat(y) : 0.0; + + // multiply the height of the rect with the phase + if (right > 0) + { + right *= phaseY; + } + else + { + left *= phaseY; + } + + barRect.origin.x = left; + barRect.size.width = right - left; + barRect.origin.y = top; + barRect.size.height = bottom - top; + + trans.rectValueToPixel(&barRect); + + if (k == 0 && !viewPortHandler.isInBoundsTop(barRect.origin.y + barRect.size.height)) + { + // Skip to next bar + break; + } + + // avoid drawing outofbounds values + if (!viewPortHandler.isInBoundsBottom(barRect.origin.y)) + { + break; + } + + // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. + CGContextSetFillColorWithColor(context, dataSet.colorAt(k).CGColor); + CGContextFillRect(context, barRect); + } + } + } + + CGContextRestoreGState(context); + } + + internal override func prepareBarHighlight(#x: CGFloat, y: Float, barspace: CGFloat, from: Float, trans: ChartTransformer, inout rect: CGRect) + { + var barWidth: CGFloat = 0.5; + + var spaceHalf = barspace / 2.0; + var top = x - barWidth + spaceHalf; + var bottom = x + barWidth - spaceHalf; + var left = y >= from ? CGFloat(y) : CGFloat(from); + var right = y <= from ? CGFloat(y) : CGFloat(from); + + rect.origin.x = left; + rect.origin.y = top; + rect.size.width = right - left; + rect.size.height = bottom - top; + + trans.rectValueToPixel(&rect, phaseY: _animator.phaseY); + } + + public override func getTransformedValues(#trans: ChartTransformer, entries: [BarChartDataEntry], dataSetIndex: Int) -> [CGPoint] + { + return trans.generateTransformedValuesHorizontalBarChart(entries, dataSet: dataSetIndex, barData: delegate!.barChartRendererData(self)!, phaseY: _animator.phaseY); + } + + public override func drawValues(#context: CGContext) + { + // if values are drawn + if (passesCheck()) + { + var barData = delegate!.barChartRendererData(self); + + var defaultValueFormatter = delegate!.barChartDefaultRendererValueFormatter(self); + + var dataSets = barData.dataSets; + + var drawValueAboveBar = delegate!.barChartIsDrawValueAboveBarEnabled(self); + var drawValuesForWholeStackEnabled = delegate!.barChartIsDrawValuesForWholeStackEnabled(self); + + // calculate the correct offset depending on the draw position of the value + var negOffset: CGFloat = (drawValueAboveBar ? -5.0 : 5.0); + var posOffset: CGFloat = (drawValueAboveBar ? 5.0 : -5.0); + + var textAlign = drawValueAboveBar ? NSTextAlignment.Left : NSTextAlignment.Right; + + for (var i = 0, count = barData.dataSetCount; i < count; i++) + { + var dataSet = dataSets[i]; + + if (!dataSet.isDrawValuesEnabled) + { + continue; + } + + var valueFont = dataSet.valueFont; + var valueTextColor = dataSet.valueTextColor; + var yOffset = -valueFont.lineHeight / 2.0; + + var formatter = dataSet.valueFormatter; + if (formatter === nil) + { + formatter = defaultValueFormatter; + } + + var trans = delegate!.barChartRenderer(self, transformerForAxis: dataSet.axisDependency); + + var entries = dataSet.yVals as! [BarChartDataEntry]; + + var valuePoints = getTransformedValues(trans: trans, entries: entries, dataSetIndex: i); + + // if only single values are drawn (sum) + if (!drawValuesForWholeStackEnabled) + { + for (var j = 0, count = Int(ceil(CGFloat(valuePoints.count) * _animator.phaseX)); j < count; j++) + { + if (!viewPortHandler.isInBoundsX(valuePoints[j].x)) + { + continue; + } + + if (!viewPortHandler.isInBoundsTop(valuePoints[j].y)) + { + break; + } + + if (!viewPortHandler.isInBoundsBottom(valuePoints[j].y)) + { + continue; + } + + var val = entries[j].value; + + drawValue( + context: context, + val: val, + xPos: valuePoints[j].x + (val >= 0.0 ? posOffset : negOffset), + yPos: valuePoints[j].y + yOffset, + formatter: formatter!, + font: valueFont, + align: .Left, + color: valueTextColor); + } + } + else + { + // if each value of a potential stack should be drawn + + for (var j = 0, count = Int(ceil(CGFloat(valuePoints.count) * _animator.phaseX)); j < count; j++) + { + var e = entries[j]; + + var vals = e.values; + + // we still draw stacked bars, but there is one non-stacked in between + if (vals == nil) + { + if (!viewPortHandler.isInBoundsX(valuePoints[j].x)) + { + continue; + } + + if (!viewPortHandler.isInBoundsTop(valuePoints[j].y)) + { + break; + } + + if (!viewPortHandler.isInBoundsBottom(valuePoints[j].y)) + { + continue; + } + + var val = e.value; + + drawValue( + context: context, + val: val, + xPos: valuePoints[j].x + (val >= 0.0 ? posOffset : negOffset), + yPos: valuePoints[j].y + yOffset, + formatter: formatter!, + font: valueFont, + align: .Left, + color: valueTextColor); + } + else + { + var transformed = [CGPoint](); + var cnt = 0; + var add = e.value; + + for (var k = 0; k < vals.count; k++) + { + add -= vals[cnt]; + transformed.append(CGPoint(x: (CGFloat(vals[cnt]) + CGFloat(add)) * _animator.phaseY, y: 0.0)); + cnt++; + } + + trans.pointValuesToPixel(&transformed); + + for (var k = 0; k < transformed.count; k++) + { + var x = transformed[k].x + (vals[k] >= 0 ? posOffset : negOffset); + var y = valuePoints[j].y; + + if (!viewPortHandler.isInBoundsX(x)) + { + continue; + } + + if (!viewPortHandler.isInBoundsTop(y)) + { + break; + } + + if (!viewPortHandler.isInBoundsBottom(y)) + { + continue; + } + + drawValue(context: context, + val: vals[k], + xPos: x, + yPos: y, + formatter: formatter!, + font: valueFont, + align: .Left, + color: valueTextColor); + } + } + } + } + } + } + } + + internal override func passesCheck() -> Bool + { + var barData = delegate!.barChartRendererData(self); + + if (barData === nil) + { + return false; + } + + return CGFloat(barData.yValCount) < CGFloat(delegate!.barChartRendererMaxVisibleValueCount(self)) * viewPortHandler.scaleY; + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/LineChartRenderer.swift b/Charts/Classes/Renderers/LineChartRenderer.swift new file mode 100644 index 0000000000..deb546f36d --- /dev/null +++ b/Charts/Classes/Renderers/LineChartRenderer.swift @@ -0,0 +1,618 @@ +// +// LineChartRenderer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +@objc +public protocol LineChartRendererDelegate +{ + func lineChartRendererData(renderer: LineChartRenderer) -> LineChartData!; + func lineChartRenderer(renderer: LineChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer!; + func lineChartRendererFillFormatter(renderer: LineChartRenderer) -> ChartFillFormatter; + func lineChartDefaultRendererValueFormatter(renderer: LineChartRenderer) -> NSNumberFormatter!; + func lineChartRendererChartYMax(renderer: LineChartRenderer) -> Float; + func lineChartRendererChartYMin(renderer: LineChartRenderer) -> Float; + func lineChartRendererChartXMax(renderer: LineChartRenderer) -> Float; + func lineChartRendererChartXMin(renderer: LineChartRenderer) -> Float; + func lineChartRendererMaxVisibleValueCount(renderer: LineChartRenderer) -> Int; +} + +public class LineChartRenderer: ChartDataRendererBase +{ + public weak var delegate: LineChartRendererDelegate?; + + public init(delegate: LineChartRendererDelegate?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + { + super.init(animator: animator, viewPortHandler: viewPortHandler); + + self.delegate = delegate; + } + + public override func drawData(#context: CGContext) + { + var lineData = delegate!.lineChartRendererData(self); + + if (lineData === nil) + { + return; + } + + for (var i = 0; i < lineData.dataSetCount; i++) + { + var set = lineData.getDataSetByIndex(i); + + if (set !== nil && set!.isVisible) + { + drawDataSet(context: context, dataSet: set as! LineChartDataSet); + } + } + } + + internal struct CGCPoint + { + internal var x: CGFloat = 0.0; + internal var y: CGFloat = 0.0; + + /// x-axis distance + internal var dx: CGFloat = 0.0; + /// y-axis distance + internal var dy: CGFloat = 0.0; + + internal init(x: CGFloat, y: CGFloat) + { + self.x = x; + self.y = y; + } + } + + internal func drawDataSet(#context: CGContext, dataSet: LineChartDataSet) + { + var lineData = delegate!.lineChartRendererData(self); + + var entries = dataSet.yVals; + + if (entries.count < 1) + { + return; + } + + calcXBounds(delegate!.lineChartRenderer(self, transformerForAxis: dataSet.axisDependency)); + + CGContextSaveGState(context); + + CGContextSetLineWidth(context, dataSet.lineWidth); + if (dataSet.lineDashLengths != nil) + { + CGContextSetLineDash(context, dataSet.lineDashPhase, dataSet.lineDashLengths, dataSet.lineDashLengths.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + // if drawing cubic lines is enabled + if (dataSet.isDrawCubicEnabled) + { + drawCubic(context: context, dataSet: dataSet, entries: entries); + } + else + { // draw normal (straight) lines + drawLinear(context: context, dataSet: dataSet, entries: entries); + } + + CGContextRestoreGState(context); + } + + internal func drawCubic(#context: CGContext, dataSet: LineChartDataSet, entries: [ChartDataEntry]) + { + var trans = delegate?.lineChartRenderer(self, transformerForAxis: dataSet.axisDependency); + + var minx = _minX; + var maxx = _maxX + 2; + + if (maxx > entries.count) + { + maxx = entries.count; + } + + var phaseX = _animator.phaseX; + var phaseY = _animator.phaseY; + + // get the color that is specified for this position from the DataSet + var drawingColor = dataSet.colors.first!; + + var intensity = dataSet.cubicIntensity; + + // the path for the cubic-spline + var cubicPath = CGPathCreateMutable(); + + var valueToPixelMatrix = trans!.valueToPixelMatrix; + + var size = Int(ceil(CGFloat(entries.count) * phaseX)); + + if (entries.count > 2) + { + var prevDx: CGFloat = 0.0; + var prevDy: CGFloat = 0.0; + var curDx: CGFloat = 0.0; + var curDy: CGFloat = 0.0; + + var cur = entries[0]; + var next = entries[1]; + var prev = entries[0]; + var prevPrev = entries[0]; + + // let the spline start + CGPathMoveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY); + + prevDx = CGFloat(next.xIndex - cur.xIndex) * intensity; + prevDy = CGFloat(next.value - cur.value) * intensity; + + cur = entries[1]; + next = entries[2]; + curDx = CGFloat(next.xIndex - prev.xIndex) * intensity; + curDy = CGFloat(next.value - prev.value) * intensity; + + // the first cubic + CGPathAddCurveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(prev.xIndex) + prevDx, (CGFloat(prev.value) + prevDy) * phaseY, CGFloat(cur.xIndex) - curDx, (CGFloat(cur.value) - curDy) * phaseY, CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY); + + for (var j = 2; j < size - 1; j++) + { + prevPrev = entries[j - 2]; + prev = entries[j - 1]; + cur = entries[j]; + next = entries[j + 1]; + + prevDx = CGFloat(cur.xIndex - prevPrev.xIndex) * intensity; + prevDy = CGFloat(cur.value - prevPrev.value) * intensity; + curDx = CGFloat(next.xIndex - prev.xIndex) * intensity; + curDy = CGFloat(next.value - prev.value) * intensity; + + CGPathAddCurveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(prev.xIndex) + prevDx, (CGFloat(prev.value) + prevDy) * phaseY, + CGFloat(cur.xIndex) - curDx, + (CGFloat(cur.value) - curDy) * phaseY, CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY); + } + + if (size > entries.count - 1) + { + cur = entries[entries.count - 1]; + prev = entries[entries.count - 2]; + prevPrev = entries[entries.count - 3]; + next = cur; + + prevDx = CGFloat(cur.xIndex - prevPrev.xIndex) * intensity; + prevDy = CGFloat(cur.value - prevPrev.value) * intensity; + curDx = CGFloat(next.xIndex - prev.xIndex) * intensity; + curDy = CGFloat(next.value - prev.value) * intensity; + + // the last cubic + CGPathAddCurveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(prev.xIndex) + prevDx, (CGFloat(prev.value) + prevDy) * phaseY, + CGFloat(cur.xIndex) - curDx, + (CGFloat(cur.value) - curDy) * phaseY, CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY); + } + } + + + CGContextSaveGState(context); + + if (dataSet.isDrawFilledEnabled) + { + drawCubicFill(context: context, dataSet: dataSet, spline: cubicPath, matrix: valueToPixelMatrix); + } + + CGContextBeginPath(context); + CGContextAddPath(context, cubicPath); + CGContextSetStrokeColorWithColor(context, drawingColor.CGColor); + CGContextStrokePath(context); + + CGContextRestoreGState(context); + } + + internal func drawCubicFill(#context: CGContext, dataSet: LineChartDataSet, spline: CGMutablePath, var matrix: CGAffineTransform) + { + CGContextSaveGState(context); + + var fillMin = delegate!.lineChartRendererFillFormatter(self).getFillLinePosition( + dataSet: dataSet, + data: delegate!.lineChartRendererData(self), + chartMaxY: delegate!.lineChartRendererChartYMax(self), + chartMinY: delegate!.lineChartRendererChartYMin(self)); + + var entryFrom = dataSet.entryForXIndex(_minX); + var entryTo = dataSet.entryForXIndex(_maxX + 1); + + var pt1 = CGPoint(x: CGFloat(entryTo.xIndex), y: fillMin); + var pt2 = CGPoint(x: CGFloat(entryFrom.xIndex), y: fillMin); + pt1 = CGPointApplyAffineTransform(pt1, matrix); + pt2 = CGPointApplyAffineTransform(pt2, matrix); + + CGContextBeginPath(context); + CGContextAddPath(context, spline); + CGContextAddLineToPoint(context, pt1.x, pt1.y); + CGContextAddLineToPoint(context, pt2.x, pt2.y); + CGContextClosePath(context); + + CGContextSetFillColorWithColor(context, dataSet.fillColor.CGColor); + CGContextSetAlpha(context, dataSet.fillAlpha); + CGContextFillPath(context); + + CGContextRestoreGState(context); + } + + private var _lineSegments = [CGPoint](count: 2, repeatedValue: CGPoint()); + + internal func drawLinear(#context: CGContext, dataSet: LineChartDataSet, entries: [ChartDataEntry]) + { + var lineData = delegate!.lineChartRendererData(self); + var dataSetIndex = lineData.indexOfDataSet(dataSet); + + var trans = delegate!.lineChartRenderer(self, transformerForAxis: dataSet.axisDependency); + var valueToPixelMatrix = trans.valueToPixelMatrix; + + var phaseX = _animator.phaseX; + var phaseY = _animator.phaseY; + + var pointBuffer = CGPoint(); + + CGContextSaveGState(context); + + // more than 1 color + if (dataSet.colors.count > 1) + { + for (var j = 0, count = Int(ceil(CGFloat(entries.count) * phaseX)); j < count; j++) + { + if (count > 1 && j == count - 1) + { // Last point, we have already drawn a line to this point + break; + } + + var e = entries[j]; + + _lineSegments[0].x = CGFloat(e.xIndex); + _lineSegments[0].y = CGFloat(e.value) * phaseY; + _lineSegments[0] = CGPointApplyAffineTransform(_lineSegments[0], valueToPixelMatrix); + if (j + 1 < count) + { + e = entries[j + 1]; + + _lineSegments[1].x = CGFloat(e.xIndex); + _lineSegments[1].y = CGFloat(e.value) * phaseY; + _lineSegments[1] = CGPointApplyAffineTransform(_lineSegments[1], valueToPixelMatrix); + } + else + { + _lineSegments[1] = _lineSegments[0]; + } + + if (!viewPortHandler.isInBoundsRight(_lineSegments[0].x)) + { + break; + } + + // make sure the lines don't do shitty things outside bounds + if (!viewPortHandler.isInBoundsLeft(_lineSegments[1].x) + || (!viewPortHandler.isInBoundsTop(_lineSegments[0].y) && !viewPortHandler.isInBoundsBottom(_lineSegments[1].y)) + || (!viewPortHandler.isInBoundsTop(_lineSegments[0].y) && !viewPortHandler.isInBoundsBottom(_lineSegments[1].y))) + { + continue; + } + + // get the color that is set for this line-segment + CGContextSetStrokeColorWithColor(context, dataSet.colorAt(j).CGColor); + CGContextStrokeLineSegments(context, _lineSegments, 2); + } + } + else + { // only one color per dataset + + var entryFrom = dataSet.entryForXIndex(_minX); + var entryTo = dataSet.entryForXIndex(_maxX); + + var minx = dataSet.entryIndex(entry: entryFrom, isEqual: true); + var maxx = dataSet.entryIndex(entry: entryTo, isEqual: true); + + var point = CGPoint(); + point.x = CGFloat(entries[minx].xIndex); + point.y = CGFloat(entries[minx].value) * phaseY; + point = CGPointApplyAffineTransform(point, valueToPixelMatrix) + + CGContextBeginPath(context); + CGContextMoveToPoint(context, point.x, point.y); + + // create a new path + for (var x = minx + 1, count = Int(ceil(CGFloat(min(entries.count, maxx) + 1) * phaseX)); x < count; x++) + { + var e = entries[x]; + + point.x = CGFloat(e.xIndex); + point.y = CGFloat(e.value) * phaseY; + point = CGPointApplyAffineTransform(point, valueToPixelMatrix) + + CGContextAddLineToPoint(context, point.x, point.y); + } + + CGContextSetStrokeColorWithColor(context, dataSet.colorAt(0).CGColor); + CGContextStrokePath(context); + } + + CGContextRestoreGState(context); + + // if drawing filled is enabled + if (dataSet.isDrawFilledEnabled && entries.count > 0) + { + drawLinearFill(context: context, dataSet: dataSet, entries: entries, trans: trans); + } + } + + internal func drawLinearFill(#context: CGContext, dataSet: LineChartDataSet, entries: [ChartDataEntry], trans: ChartTransformer) + { + var entryFrom = dataSet.entryForXIndex(_minX - 2); + var entryTo = dataSet.entryForXIndex(_maxX + 2); + + var minx = dataSet.entryIndex(entry: entryFrom, isEqual: true); + var maxx = dataSet.entryIndex(entry: entryTo, isEqual: true); + + CGContextSaveGState(context); + + CGContextSetFillColorWithColor(context, dataSet.fillColor.CGColor); + + // filled is usually drawn with less alpha + CGContextSetAlpha(context, dataSet.fillAlpha); + + var filled = generateFilledPath( + entries, + fillMin: delegate!.lineChartRendererFillFormatter(self).getFillLinePosition( + dataSet: dataSet, + data: delegate!.lineChartRendererData(self), + chartMaxY: delegate!.lineChartRendererChartYMax(self), + chartMinY: delegate!.lineChartRendererChartYMin(self)), + from: minx, + to: maxx, + matrix: trans.valueToPixelMatrix); + + CGContextBeginPath(context); + CGContextAddPath(context, filled); + CGContextFillPath(context); + + CGContextRestoreGState(context); + } + + /// Generates the path that is used for filled drawing. + private func generateFilledPath(entries: [ChartDataEntry], fillMin: CGFloat, from: Int, to: Int, var matrix: CGAffineTransform) -> CGPath + { + var point = CGPoint(); + + var phaseX = _animator.phaseX; + var phaseY = _animator.phaseY; + + var filled = CGPathCreateMutable(); + CGPathMoveToPoint(filled, &matrix, CGFloat(entries[from].xIndex), CGFloat(entries[from].value) * phaseY); + + // create a new path + for (var x = from + 1, count = Int(ceil(CGFloat(to) * phaseX)); x <= count; x++) + { + var e = entries[x]; + CGPathAddLineToPoint(filled, &matrix, CGFloat(e.xIndex), CGFloat(e.value) * phaseY); + } + + // close up + CGPathAddLineToPoint(filled, &matrix, CGFloat(entries[Int(CGFloat(to) * phaseX)].xIndex), fillMin); + CGPathAddLineToPoint(filled, &matrix, CGFloat(entries[from].xIndex), fillMin); + CGPathCloseSubpath(filled); + + return filled; + } + + public override func drawValues(#context: CGContext) + { + var lineData = delegate!.lineChartRendererData(self); + if (lineData === nil) + { + return; + } + + var defaultValueFormatter = delegate!.lineChartDefaultRendererValueFormatter(self); + + if (CGFloat(lineData.yValCount) < CGFloat(delegate!.lineChartRendererMaxVisibleValueCount(self)) * viewPortHandler.scaleX) + { + var dataSets = lineData.dataSets; + + for (var i = 0; i < dataSets.count; i++) + { + var dataSet = dataSets[i] as! LineChartDataSet; + + if (!dataSet.isDrawValuesEnabled) + { + continue; + } + + var valueFont = dataSet.valueFont; + var valueTextColor = dataSet.valueTextColor; + + var formatter = dataSet.valueFormatter; + if (formatter === nil) + { + formatter = defaultValueFormatter; + } + + var trans = delegate!.lineChartRenderer(self, transformerForAxis: dataSet.axisDependency); + + // make sure the values do not interfear with the circles + var valOffset = Int(dataSet.circleRadius * 1.75); + + if (!dataSet.isDrawCirclesEnabled) + { + valOffset = valOffset / 2; + } + + var entries = dataSet.yVals; + + var positions = trans.generateTransformedValuesLine(entries, phaseY: _animator.phaseY); + + for (var j = 0, count = Int(ceil(CGFloat(positions.count) * _animator.phaseX)); j < count; j++) + { + if (!viewPortHandler.isInBoundsRight(positions[j].x)) + { + break; + } + + if (!viewPortHandler.isInBoundsLeft(positions[j].x) || !viewPortHandler.isInBoundsY(positions[j].y)) + { + continue; + } + + var val = entries[j].value; + + ChartUtils.drawText(context: context, text: formatter!.stringFromNumber(val)!, point: CGPoint(x: positions[j].x, y: positions[j].y - CGFloat(valOffset) - valueFont.lineHeight), align: .Center, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: valueTextColor]); + } + } + } + } + + public override func drawExtras(#context: CGContext) + { + drawCircles(context: context); + } + + private func drawCircles(#context: CGContext) + { + var phaseX = _animator.phaseX; + var phaseY = _animator.phaseY; + + var lineData = delegate!.lineChartRendererData(self); + + var dataSets = lineData.dataSets; + + var pt = CGPoint(); + var rect = CGRect(); + + CGContextSaveGState(context); + + for (var i = 0, count = dataSets.count; i < count; i++) + { + var dataSet = lineData.getDataSetByIndex(i) as! LineChartDataSet!; + + if (!dataSet.isVisible || !dataSet.isDrawCirclesEnabled) + { + continue; + } + + var trans = delegate!.lineChartRenderer(self, transformerForAxis: dataSet.axisDependency); + var valueToPixelMatrix = trans.valueToPixelMatrix; + + var entries = dataSet.yVals; + + var circleRadius = dataSet.circleRadius; + var circleDiameter = circleRadius * 2.0; + var circleHoleDiameter = circleRadius; + var circleHoleRadius = circleHoleDiameter / 2.0; + var isDrawCircleHoleEnabled = dataSet.isDrawCircleHoleEnabled; + + for (var j = 0, count = Int(ceil(CGFloat(entries.count) * _animator.phaseX)); j < count; j++) + { + var e = entries[j]; + pt.x = CGFloat(e.xIndex); + pt.y = CGFloat(e.value) * phaseY; + pt = CGPointApplyAffineTransform(pt, valueToPixelMatrix); + + if (!viewPortHandler.isInBoundsRight(pt.x)) + { + break; + } + + // make sure the circles don't do shitty things outside bounds + if (!viewPortHandler.isInBoundsLeft(pt.x) || !viewPortHandler.isInBoundsY(pt.y)) + { + continue; + } + + CGContextSetFillColorWithColor(context, dataSet.getCircleColor(j)!.CGColor); + + rect.origin.x = pt.x - circleRadius; + rect.origin.y = pt.y - circleRadius; + rect.size.width = circleDiameter; + rect.size.height = circleDiameter; + CGContextFillEllipseInRect(context, rect); + + if (isDrawCircleHoleEnabled) + { + CGContextSetFillColorWithColor(context, dataSet.circleHoleColor.CGColor); + + rect.origin.x = pt.x - circleHoleRadius; + rect.origin.y = pt.y - circleHoleRadius; + rect.size.width = circleHoleDiameter; + rect.size.height = circleHoleDiameter; + CGContextFillEllipseInRect(context, rect); + } + } + } + + CGContextRestoreGState(context); + } + + var _highlightPtsBuffer = [CGPoint](count: 4, repeatedValue: CGPoint()); + + public override func drawHighlighted(#context: CGContext, indices: [ChartHighlight]) + { + var lineData = delegate!.lineChartRendererData(self); + var chartXMax = delegate!.lineChartRendererChartXMax(self); + var chartYMax = delegate!.lineChartRendererChartYMax(self); + var chartYMin = delegate!.lineChartRendererChartYMin(self); + + CGContextSaveGState(context); + + for (var i = 0; i < indices.count; i++) + { + var set = lineData.getDataSetByIndex(indices[i].dataSetIndex) as! LineChartDataSet!; + + if (set === nil) + { + continue; + } + + CGContextSetStrokeColorWithColor(context, set.highlightColor.CGColor); + CGContextSetLineWidth(context, set.highlightLineWidth); + if (set.highlightLineDashLengths != nil) + { + CGContextSetLineDash(context, set.highlightLineDashPhase, set.highlightLineDashLengths!, set.highlightLineDashLengths!.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var xIndex = indices[i].xIndex; // get the x-position + + if (CGFloat(xIndex) > CGFloat(chartXMax) * _animator.phaseX) + { + continue; + } + + var y = CGFloat(set.yValForXIndex(xIndex)) * _animator.phaseY; // get the y-position + + _highlightPtsBuffer[0] = CGPoint(x: CGFloat(xIndex), y: CGFloat(chartYMax)); + _highlightPtsBuffer[1] = CGPoint(x: CGFloat(xIndex), y: CGFloat(chartYMin)); + _highlightPtsBuffer[2] = CGPoint(x: 0.0, y: y); + _highlightPtsBuffer[3] = CGPoint(x: CGFloat(chartXMax), y: y); + + var trans = delegate!.lineChartRenderer(self, transformerForAxis: set.axisDependency); + + trans.pointValuesToPixel(&_highlightPtsBuffer); + + // draw the highlight lines + CGContextStrokeLineSegments(context, _highlightPtsBuffer, 4); + } + + CGContextRestoreGState(context); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/PieChartRenderer.swift b/Charts/Classes/Renderers/PieChartRenderer.swift new file mode 100644 index 0000000000..6d53d546dc --- /dev/null +++ b/Charts/Classes/Renderers/PieChartRenderer.swift @@ -0,0 +1,367 @@ +// +// PieChartRenderer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class PieChartRenderer: ChartDataRendererBase +{ + internal weak var _chart: PieChartView! + + public var drawHoleEnabled = true + public var holeTransparent = true + public var holeColor: UIColor? = UIColor.whiteColor() + public var holeRadiusPercent = CGFloat(0.5) + public var transparentCircleRadiusPercent = CGFloat(0.55) + public var centerTextColor = UIColor.blackColor() + public var centerTextFont = UIFont.systemFontOfSize(12.0) + public var drawXLabelsEnabled = true + public var usePercentValuesEnabled = false + public var centerText: String! + public var drawCenterTextEnabled = true + + public init(chart: PieChartView, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + { + super.init(animator: animator, viewPortHandler: viewPortHandler); + + _chart = chart; + } + + public override func drawData(#context: CGContext) + { + if (_chart !== nil) + { + var pieData = _chart.data; + + if (pieData != nil) + { + for set in pieData!.dataSets as! [PieChartDataSet] + { + if (set.isVisible) + { + drawDataSet(context: context, dataSet: set); + } + } + } + } + } + + internal func drawDataSet(#context: CGContext, dataSet: PieChartDataSet) + { + var angle = _chart.rotationAngle; + + var cnt = 0; + + var entries = dataSet.yVals; + var drawAngles = _chart.drawAngles; + var circleBox = _chart.circleBox; + var radius = _chart.radius; + var innerRadius = drawHoleEnabled && holeTransparent ? radius * holeRadiusPercent : 0.0; + + CGContextSaveGState(context); + + for (var j = 0; j < entries.count; j++) + { + var newangle = drawAngles[cnt]; + var sliceSpace = dataSet.sliceSpace; + + var e = entries[j]; + + // draw only if the value is greater than zero + if ((abs(e.value) > 0.000001)) + { + if (!_chart.needsHighlight(xIndex: e.xIndex, + dataSetIndex: _chart.data!.indexOfDataSet(dataSet))) + { + var startAngle = angle + sliceSpace / 2.0; + var sweepAngle = newangle * _animator.phaseY + - sliceSpace / 2.0; + var endAngle = startAngle + sweepAngle; + + var path = CGPathCreateMutable(); + CGPathMoveToPoint(path, nil, circleBox.midX, circleBox.midY); + CGPathAddArc(path, nil, circleBox.midX, circleBox.midY, radius, startAngle * ChartUtils.Math.FDEG2RAD, endAngle * ChartUtils.Math.FDEG2RAD, false); + CGPathCloseSubpath(path); + + if (innerRadius > 0.0) + { + CGPathMoveToPoint(path, nil, circleBox.midX, circleBox.midY); + CGPathAddArc(path, nil, circleBox.midX, circleBox.midY, innerRadius, startAngle * ChartUtils.Math.FDEG2RAD, endAngle * ChartUtils.Math.FDEG2RAD, false); + CGPathCloseSubpath(path); + } + + CGContextBeginPath(context); + CGContextAddPath(context, path); + CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor); + CGContextEOFillPath(context); + } + } + + angle += newangle * _animator.phaseX; + cnt++; + } + + CGContextRestoreGState(context); + } + + public override func drawValues(#context: CGContext) + { + var center = _chart.centerCircleBox; + + // get whole the radius + var r = _chart.radius; + var rotationAngle = _chart.rotationAngle; + var drawAngles = _chart.drawAngles; + var absoluteAngles = _chart.absoluteAngles; + + var off = r / 3.0; + + if (drawHoleEnabled) + { + off = (r - (r * _chart.holeRadiusPercent)) / 2.0; + } + + r -= off; // offset to keep things inside the chart + + var data: ChartData! = _chart.data; + if (data === nil) + { + return; + } + + var defaultValueFormatter = _chart.valueFormatter; + + var dataSets = data.dataSets; + + var cnt = 0; + + for (var i = 0; i < dataSets.count; i++) + { + var dataSet = dataSets[i] as! PieChartDataSet; + + if (!dataSet.isDrawValuesEnabled) + { + continue; + } + + var valueFont = dataSet.valueFont; + var valueTextColor = dataSet.valueTextColor; + + var formatter = dataSet.valueFormatter; + if (formatter === nil) + { + formatter = defaultValueFormatter; + } + + var entries = dataSet.yVals; + + for (var j = 0, maxEntry = Int(CGFloat(entries.count) * _animator.phaseX); j < maxEntry; j++) + { + // offset needed to center the drawn text in the slice + var offset = drawAngles[cnt] / 2.0; + + // calculate the text position + var x = (r * cos(((rotationAngle + absoluteAngles[cnt] - offset) * _animator.phaseY) * ChartUtils.Math.FDEG2RAD) + center.x); + var y = (r * sin(((rotationAngle + absoluteAngles[cnt] - offset) * _animator.phaseY) * ChartUtils.Math.FDEG2RAD) + center.y); + + var value = usePercentValuesEnabled ? entries[j].value / _chart.yValueSum * 100.0 : entries[j].value; + + var val = formatter!.stringFromNumber(value)!; + + var drawXVals = drawXLabelsEnabled; + var drawYVals = dataSet.isDrawValuesEnabled; + + var lineHeight = valueFont.lineHeight; + y -= lineHeight; + + // draw everything, depending on settings + if (drawXVals && drawYVals) + { + y += lineHeight / 2.0; + + ChartUtils.drawText(context: context, text: val, point: CGPoint(x: x, y: y), align: .Center, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: valueTextColor]); + + if (j < data.xValCount) + { + ChartUtils.drawText(context: context, text: data.xVals[j], point: CGPoint(x: x, y: y - lineHeight), align: .Center, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: valueTextColor]); + } + } + else if (drawXVals && !drawYVals) + { + if (j < data.xValCount) + { + ChartUtils.drawText(context: context, text: data.xVals[j], point: CGPoint(x: x, y: y), align: .Center, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: valueTextColor]); + } + } + else if (!drawXVals && drawYVals) + { + ChartUtils.drawText(context: context, text: val, point: CGPoint(x: x, y: y), align: .Center, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: valueTextColor]); + } + + cnt++; + } + } + } + + public override func drawExtras(#context: CGContext) + { + drawHole(context: context); + drawCenterText(context: context); + } + + /// draws the hole in the center of the chart and the transparent circle / hole + private func drawHole(#context: CGContext) + { + if (_chart.drawHoleEnabled) + { + CGContextSaveGState(context); + + var radius = _chart.radius; + var holeRadius = radius * holeRadiusPercent; + var center = _chart.centerCircleBox; + + if (holeColor !== nil && holeColor != UIColor.clearColor()) + { + // draw the hole-circle + CGContextSetFillColorWithColor(context, holeColor!.CGColor); + CGContextFillEllipseInRect(context, CGRect(x: center.x - holeRadius, y: center.y - holeRadius, width: holeRadius * 2.0, height: holeRadius * 2.0)); + } + + if (transparentCircleRadiusPercent > holeRadiusPercent) + { + var secondHoleRadius = radius * transparentCircleRadiusPercent; + + // make transparent + CGContextSetFillColorWithColor(context, holeColor!.colorWithAlphaComponent(CGFloat(0x60) / CGFloat(0xFF)).CGColor); + + // draw the transparent-circle + CGContextFillEllipseInRect(context, CGRect(x: center.x - secondHoleRadius, y: center.y - secondHoleRadius, width: secondHoleRadius * 2.0, height: secondHoleRadius * 2.0)); + } + + CGContextRestoreGState(context); + } + } + + /// draws the description text in the center of the pie chart makes most + /// sense when center-hole is enabled + private func drawCenterText(#context: CGContext) + { + if (drawCenterTextEnabled && centerText != nil) + { + var center = _chart.centerCircleBox; + + // get all lines from the text + var lines = centerText.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()); + + // calculate the height for each line + var lineHeight = centerTextFont.lineHeight; + + var totalheight = lineHeight * CGFloat(lines.count); + + var cnt = lines.count; + + var y = center.y; + + for (var i = 0; i < lines.count; i++) + { + var line = lines[lines.count - i - 1]; + + ChartUtils.drawText(context: context, text: line, point: CGPoint(x: center.x, y: y - lineHeight + lineHeight * CGFloat(cnt) - totalheight / 2.0), align: .Center, attributes: [NSFontAttributeName: centerTextFont, NSForegroundColorAttributeName: centerTextColor]); + + cnt--; + } + } + } + + public override func drawHighlighted(#context: CGContext, indices: [ChartHighlight]) + { + if (_chart.data === nil) + { + return; + } + + CGContextSaveGState(context); + + var rotationAngle = _chart.rotationAngle; + var angle = CGFloat(0.0); + + var drawAngles = _chart.drawAngles; + var absoluteAngles = _chart.absoluteAngles; + + var innerRadius = drawHoleEnabled && holeTransparent ? _chart.radius * holeRadiusPercent : 0.0; + + for (var i = 0; i < indices.count; i++) + { + // get the index to highlight + var xIndex = indices[i].xIndex; + if (xIndex >= drawAngles.count) + { + continue; + } + + if (xIndex == 0) + { + angle = rotationAngle; + } + else + { + angle = rotationAngle + absoluteAngles[xIndex - 1]; + } + + angle *= _animator.phaseY; + + var sliceDegrees = drawAngles[xIndex]; + + var set = _chart.data?.getDataSetByIndex(indices[i].dataSetIndex) as! PieChartDataSet!; + + if (set === nil) + { + continue; + } + + var shift = set.selectionShift; + var circleBox = _chart.circleBox; + + var highlighted = CGRect( + x: circleBox.origin.x - shift, + y: circleBox.origin.y - shift, + width: circleBox.size.width + shift * 2.0, + height: circleBox.size.height + shift * 2.0); + + CGContextSetFillColorWithColor(context, set.colorAt(xIndex).CGColor); + + // redefine the rect that contains the arc so that the highlighted pie is not cut off + + var startAngle = angle + set.sliceSpace / 2.0; + var sweepAngle = sliceDegrees * _animator.phaseY - set.sliceSpace / 2.0; + var endAngle = startAngle + sweepAngle; + + var path = CGPathCreateMutable(); + CGPathMoveToPoint(path, nil, highlighted.midX, highlighted.midY); + CGPathAddArc(path, nil, highlighted.midX, highlighted.midY, highlighted.size.width / 2.0, startAngle * ChartUtils.Math.FDEG2RAD, endAngle * ChartUtils.Math.FDEG2RAD, false); + CGPathCloseSubpath(path); + + if (innerRadius > 0.0) + { + CGPathMoveToPoint(path, nil, highlighted.midX, highlighted.midY); + CGPathAddArc(path, nil, highlighted.midX, highlighted.midY, innerRadius, startAngle * ChartUtils.Math.FDEG2RAD, endAngle * ChartUtils.Math.FDEG2RAD, false); + CGPathCloseSubpath(path); + } + + CGContextBeginPath(context); + CGContextAddPath(context, path); + CGContextEOFillPath(context); + } + + CGContextRestoreGState(context); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/RadarChartRenderer.swift b/Charts/Classes/Renderers/RadarChartRenderer.swift new file mode 100644 index 0000000000..23888b4f23 --- /dev/null +++ b/Charts/Classes/Renderers/RadarChartRenderer.swift @@ -0,0 +1,280 @@ +// +// RadarChartRenderer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class RadarChartRenderer: ChartDataRendererBase +{ + internal weak var _chart: RadarChartView!; + + public init(chart: RadarChartView, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + { + super.init(animator: animator, viewPortHandler: viewPortHandler); + + _chart = chart; + } + + public override func drawData(#context: CGContext) + { + if (_chart !== nil) + { + var radarData = _chart.data; + + if (radarData != nil) + { + for set in radarData!.dataSets as! [RadarChartDataSet] + { + if (set.isVisible) + { + drawDataSet(context: context, dataSet: set); + } + } + } + } + } + + internal func drawDataSet(#context: CGContext, dataSet: RadarChartDataSet) + { + CGContextSaveGState(context); + + var sliceangle = _chart.sliceAngle; + + // calculate the factor that is needed for transforming the value to pixels + var factor = _chart.factor; + + var center = _chart.centerOffsets; + + var entries = dataSet.yVals; + + var path = CGPathCreateMutable(); + + for (var j = 0; j < entries.count; j++) + { + var e = entries[j]; + + var p = ChartUtils.getPosition(center: center, dist: CGFloat(e.value - _chart.chartYMin) * factor, angle: sliceangle * CGFloat(j) + _chart.rotationAngle); + + if (j == 0) + { + CGPathMoveToPoint(path, nil, p.x, p.y); + } + else + { + CGPathAddLineToPoint(path, nil, p.x, p.y); + } + } + + CGPathCloseSubpath(path); + + // draw filled + if (dataSet.isDrawFilledEnabled) + { + CGContextSetFillColorWithColor(context, dataSet.colorAt(0).CGColor); + CGContextSetAlpha(context, dataSet.fillAlpha); + + CGContextBeginPath(context); + CGContextAddPath(context, path); + CGContextFillPath(context); + } + + // draw the line (only if filled is disabled or alpha is below 255) + if (!dataSet.isDrawFilledEnabled || dataSet.fillAlpha < 1.0) + { + CGContextSetStrokeColorWithColor(context, dataSet.colorAt(0).CGColor) + CGContextSetLineWidth(context, dataSet.lineWidth); + CGContextSetAlpha(context, 1.0); + + CGContextBeginPath(context); + CGContextAddPath(context, path); + CGContextStrokePath(context); + } + + CGContextRestoreGState(context); + } + + public override func drawValues(#context: CGContext) + { + if (_chart.data === nil) + { + return; + } + + var data = _chart.data!; + + var defaultValueFormatter = _chart.valueFormatter; + + var sliceangle = _chart.sliceAngle; + + // calculate the factor that is needed for transforming the value to pixels + var factor = _chart.factor; + + var center = _chart.centerOffsets; + + var yoffset = CGFloat(5.0); + + for (var i = 0, count = data.dataSetCount; i < count; i++) + { + var dataSet = data.getDataSetByIndex(i) as! RadarChartDataSet; + + if (!dataSet.isDrawValuesEnabled) + { + continue; + } + + var entries = dataSet.yVals; + + for (var j = 0; j < entries.count; j++) + { + var e = entries[j]; + + var p = ChartUtils.getPosition(center: center, dist: CGFloat(e.value) * factor, angle: sliceangle * CGFloat(j) + _chart.rotationAngle); + + var valueFont = dataSet.valueFont; + var valueTextColor = dataSet.valueTextColor; + + var formatter = dataSet.valueFormatter; + if (formatter === nil) + { + formatter = defaultValueFormatter; + } + + ChartUtils.drawText(context: context, text: formatter!.stringFromNumber(e.value)!, point: CGPoint(x: p.x, y: p.y - yoffset - valueFont.lineHeight), align: .Center, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: valueTextColor]); + } + } + } + + public override func drawExtras(#context: CGContext) + { + drawWeb(context: context); + } + + internal func drawWeb(#context: CGContext) + { + var sliceangle = _chart.sliceAngle; + + CGContextSaveGState(context); + + // calculate the factor that is needed for transforming the value to + // pixels + var factor = _chart.factor; + var rotationangle = _chart.rotationAngle; + + var center = _chart.centerOffsets; + + var lineSegments = UnsafeMutablePointer.alloc(2) + + // draw the web lines that come from the center + CGContextSetLineWidth(context, _chart.webLineWidth); + CGContextSetStrokeColorWithColor(context, _chart.webColor.CGColor); + CGContextSetAlpha(context, _chart.webAlpha); + + for (var i = 0, xValCount = _chart.data!.xValCount; i < xValCount; i++) + { + var p = ChartUtils.getPosition(center: center, dist: CGFloat(_chart.yRange) * factor, angle: sliceangle * CGFloat(i) + rotationangle); + + lineSegments[0].x = center.x; + lineSegments[0].y = center.y; + lineSegments[1].x = p.x; + lineSegments[1].y = p.y; + + CGContextStrokeLineSegments(context, lineSegments, 2); + } + + // draw the inner-web + CGContextSetLineWidth(context, _chart.innerWebLineWidth); + CGContextSetStrokeColorWithColor(context, _chart.innerWebColor.CGColor); + CGContextSetAlpha(context, _chart.webAlpha); + + var labelCount = _chart.yAxis.entryCount; + + for (var j = 0; j < labelCount; j++) + { + for (var i = 0, xValCount = _chart.data!.xValCount; i < xValCount; i++) + { + var r = CGFloat(_chart.yAxis.entries[j] - _chart.chartYMin) * factor; + + var p1 = ChartUtils.getPosition(center: center, dist: r, angle: sliceangle * CGFloat(i) + rotationangle); + var p2 = ChartUtils.getPosition(center: center, dist: r, angle: sliceangle * CGFloat(i + 1) + rotationangle); + + lineSegments[0].x = p1.x; + lineSegments[0].y = p1.y; + lineSegments[1].x = p2.x; + lineSegments[1].y = p2.y; + + CGContextStrokeLineSegments(context, lineSegments, 2); + } + } + + lineSegments.dealloc(2); + + CGContextRestoreGState(context); + } + + private var _lineSegments = [CGPoint](count: 4, repeatedValue: CGPoint()); + + public override func drawHighlighted(#context: CGContext, indices: [ChartHighlight]) + { + if (_chart.data === nil) + { + return; + } + + var data = _chart.data as! RadarChartData; + + CGContextSaveGState(context); + CGContextSetLineWidth(context, data.highlightLineWidth); + if (data.highlightLineDashLengths != nil) + { + CGContextSetLineDash(context, data.highlightLineDashPhase, data.highlightLineDashLengths!, data.highlightLineDashLengths!.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var sliceangle = _chart.sliceAngle; + var factor = _chart.factor; + + var center = _chart.centerOffsets; + + for (var i = 0; i < indices.count; i++) + { + var set = _chart.data?.getDataSetByIndex(indices[i].dataSetIndex) as! RadarChartDataSet!; + + if (set === nil) + { + continue; + } + + CGContextSetStrokeColorWithColor(context, set.highlightColor.CGColor); + + // get the index to highlight + var xIndex = indices[i].xIndex; + + var e = set.entryForXIndex(xIndex); + var j = set.entryIndex(entry: e, isEqual: true); + var y = (e.value - _chart.chartYMin); + + var p = ChartUtils.getPosition(center: center, dist: CGFloat(y) * factor, + angle: sliceangle * CGFloat(j) + _chart.rotationAngle); + + _lineSegments[0] = CGPoint(x: p.x, y: 0.0) + _lineSegments[1] = CGPoint(x: p.x, y: viewPortHandler.chartHeight) + _lineSegments[2] = CGPoint(x: 0.0, y: p.y) + _lineSegments[3] = CGPoint(x: viewPortHandler.chartWidth, y: p.y) + CGContextStrokeLineSegments(context, _lineSegments, 4); + } + + CGContextRestoreGState(context); + } +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ScatterChartRenderer.swift b/Charts/Classes/Renderers/ScatterChartRenderer.swift new file mode 100644 index 0000000000..c267f24d75 --- /dev/null +++ b/Charts/Classes/Renderers/ScatterChartRenderer.swift @@ -0,0 +1,298 @@ +// +// ScatterChartRenderer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 4/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +@objc +public protocol ScatterChartRendererDelegate +{ + func scatterChartRendererData(renderer: ScatterChartRenderer) -> ScatterChartData!; + func scatterChartRenderer(renderer: ScatterChartRenderer, transformerForAxis which: ChartYAxis.AxisDependency) -> ChartTransformer!; + func scatterChartDefaultRendererValueFormatter(renderer: ScatterChartRenderer) -> NSNumberFormatter!; + func scatterChartRendererChartYMax(renderer: ScatterChartRenderer) -> Float; + func scatterChartRendererChartYMin(renderer: ScatterChartRenderer) -> Float; + func scatterChartRendererChartXMax(renderer: ScatterChartRenderer) -> Float; + func scatterChartRendererChartXMin(renderer: ScatterChartRenderer) -> Float; + func scatterChartRendererMaxVisibleValueCount(renderer: ScatterChartRenderer) -> Int; +} + +public class ScatterChartRenderer: ChartDataRendererBase +{ + public weak var delegate: ScatterChartRendererDelegate?; + + public init(delegate: ScatterChartRendererDelegate?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + { + super.init(animator: animator, viewPortHandler: viewPortHandler); + + self.delegate = delegate; + } + + public override func drawData(#context: CGContext) + { + var scatterData = delegate!.scatterChartRendererData(self); + + if (scatterData === nil) + { + return; + } + + for (var i = 0; i < scatterData.dataSetCount; i++) + { + var set = scatterData.getDataSetByIndex(i); + + if (set !== nil && set!.isVisible) + { + drawDataSet(context: context, dataSet: set as! ScatterChartDataSet); + } + } + } + + private var _lineSegments = [CGPoint](count: 2, repeatedValue: CGPoint()); + + internal func drawDataSet(#context: CGContext, dataSet: ScatterChartDataSet) + { + var trans = delegate!.scatterChartRenderer(self, transformerForAxis: dataSet.axisDependency); + calcXBounds(trans); + + var phaseX = _animator.phaseX; + var phaseY = _animator.phaseY; + + var entries = dataSet.yVals; + + var shapeSize = dataSet.scatterShapeSize; + var shapeHalf = shapeSize / 2.0; + + var point = CGPoint(); + + var valueToPixelMatrix = trans.valueToPixelMatrix; + + var shape = dataSet.scatterShape; + + CGContextSaveGState(context); + + for (var j = 0, count = Int(ceil(CGFloat(entries.count) * _animator.phaseX)); j < count; j++) + { + var e = entries[j]; + point.x = CGFloat(e.xIndex); + point.y = CGFloat(e.value) * phaseY; + point = CGPointApplyAffineTransform(point, valueToPixelMatrix); + + if (!viewPortHandler.isInBoundsRight(point.x)) + { + break; + } + + if (!viewPortHandler.isInBoundsLeft(point.x) || !viewPortHandler.isInBoundsY(point.y)) + { + continue; + } + + if (shape == .Square) + { + CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor); + var rect = CGRect(); + rect.origin.x = point.x - shapeHalf; + rect.origin.y = point.y - shapeHalf; + rect.size.width = shapeSize; + rect.size.height = shapeSize; + CGContextFillRect(context, rect); + } + else if (shape == .Circle) + { + CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor); + var rect = CGRect(); + rect.origin.x = point.x - shapeHalf; + rect.origin.y = point.y - shapeHalf; + rect.size.width = shapeSize; + rect.size.height = shapeSize; + CGContextFillEllipseInRect(context, rect); + } + else if (shape == .Cross) + { + CGContextSetStrokeColorWithColor(context, dataSet.colorAt(j).CGColor); + _lineSegments[0].x = point.x - shapeHalf; + _lineSegments[0].y = point.y; + _lineSegments[1].x = point.x + shapeHalf; + _lineSegments[1].y = point.y; + CGContextStrokeLineSegments(context, _lineSegments, 2); + + _lineSegments[0].x = point.x; + _lineSegments[0].y = point.y - shapeHalf; + _lineSegments[1].x = point.x; + _lineSegments[1].y = point.y + shapeHalf; + CGContextStrokeLineSegments(context, _lineSegments, 2); + } + else if (shape == .Triangle) + { + CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor); + + // create a triangle path + CGContextBeginPath(context); + CGContextMoveToPoint(context, point.x, point.y - shapeHalf); + CGContextAddLineToPoint(context, point.x + shapeHalf, point.y + shapeHalf); + CGContextAddLineToPoint(context, point.x - shapeHalf, point.y + shapeHalf); + CGContextClosePath(context); + + CGContextFillPath(context); + } + else if (shape == .Custom) + { + CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor); + + var customShape = dataSet.customScatterShape + + if (customShape === nil) + { + return; + } + + // transform the provided custom path + CGContextSaveGState(context); + CGContextTranslateCTM(context, -point.x, -point.y); + + CGContextBeginPath(context); + CGContextAddPath(context, customShape); + CGContextFillPath(context); + + CGContextRestoreGState(context); + } + } + + CGContextRestoreGState(context); + } + + public override func drawValues(#context: CGContext) + { + var scatterData = delegate!.scatterChartRendererData(self); + if (scatterData === nil) + { + return; + } + + var defaultValueFormatter = delegate!.scatterChartDefaultRendererValueFormatter(self); + + // if values are drawn + if (scatterData.yValCount < Int(ceil(CGFloat(delegate!.scatterChartRendererMaxVisibleValueCount(self)) * viewPortHandler.scaleX))) + { + var dataSets = scatterData.dataSets as! [ScatterChartDataSet]; + + for (var i = 0; i < scatterData.dataSetCount; i++) + { + var dataSet = dataSets[i]; + + if (!dataSet.isDrawValuesEnabled) + { + continue; + } + + var valueFont = dataSet.valueFont; + var valueTextColor = dataSet.valueTextColor; + + var formatter = dataSet.valueFormatter; + if (formatter === nil) + { + formatter = defaultValueFormatter; + } + + var entries = dataSet.yVals; + + var positions = delegate!.scatterChartRenderer(self, transformerForAxis: dataSet.axisDependency).generateTransformedValuesScatter(entries, phaseY: _animator.phaseY); + + var shapeSize = dataSet.scatterShapeSize; + var lineHeight = valueFont.lineHeight; + + for (var j = 0, count = Int(ceil(CGFloat(positions.count) * _animator.phaseX)); j < count; j++) + { + if (!viewPortHandler.isInBoundsRight(positions[j].x)) + { + break; + } + + // make sure the lines don't do shitty things outside bounds + if (j != 0 && (!viewPortHandler.isInBoundsLeft(positions[j].x) + || !viewPortHandler.isInBoundsY(positions[j].y))) + { + continue; + } + + var val = entries[j].value; + + var text = formatter!.stringFromNumber(val); + + ChartUtils.drawText(context: context, text: text!, point: CGPoint(x: positions[j].x, y: positions[j].y - shapeSize - lineHeight), align: .Center, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: valueTextColor]); + } + } + } + } + + public override func drawExtras(#context: CGContext) + { + + } + + public override func drawHighlighted(#context: CGContext, indices: [ChartHighlight]) + { + var scatterData = delegate!.scatterChartRendererData(self); + var chartXMax = delegate!.scatterChartRendererChartXMax(self); + var chartYMax = delegate!.scatterChartRendererChartYMax(self); + var chartYMin = delegate!.scatterChartRendererChartYMin(self); + + CGContextSaveGState(context); + + var pts = [CGPoint](count: 4, repeatedValue: CGPoint()); + + for (var i = 0; i < indices.count; i++) + { + var set = scatterData.getDataSetByIndex(indices[i].dataSetIndex) as! ScatterChartDataSet!; + + if (set === nil) + { + continue; + } + + CGContextSetStrokeColorWithColor(context, set.highlightColor.CGColor); + CGContextSetLineWidth(context, set.highlightLineWidth); + if (set.highlightLineDashLengths != nil) + { + CGContextSetLineDash(context, set.highlightLineDashPhase, set.highlightLineDashLengths!, set.highlightLineDashLengths!.count); + } + else + { + CGContextSetLineDash(context, 0.0, nil, 0); + } + + var xIndex = indices[i].xIndex; // get the x-position + + if (CGFloat(xIndex) > CGFloat(chartXMax) * _animator.phaseX) + { + continue; + } + + var y = CGFloat(set.yValForXIndex(xIndex)) * _animator.phaseY; // get the y-position + + pts[0] = CGPoint(x: CGFloat(xIndex), y: CGFloat(chartYMax)); + pts[1] = CGPoint(x: CGFloat(xIndex), y: CGFloat(chartYMin)); + pts[2] = CGPoint(x: 0.0, y: y); + pts[3] = CGPoint(x: CGFloat(chartXMax), y: y); + + var trans = delegate!.scatterChartRenderer(self, transformerForAxis: set.axisDependency); + + trans.pointValuesToPixel(&pts); + + // draw the highlight lines + CGContextStrokeLineSegments(context, pts, pts.count); + } + + CGContextRestoreGState(context); + } +} \ No newline at end of file diff --git a/Charts/Classes/Utils/ChartColorTemplates.swift b/Charts/Classes/Utils/ChartColorTemplates.swift new file mode 100644 index 0000000000..c6599520f2 --- /dev/null +++ b/Charts/Classes/Utils/ChartColorTemplates.swift @@ -0,0 +1,74 @@ +// +// ChartColorTemplates.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit + +public class ChartColorTemplates: NSObject +{ + public class func liberty () -> [UIColor] + { + return [ + UIColor(red: 207/255.0, green: 248/255.0, blue: 246/255.0, alpha: 1.0), + UIColor(red: 148/255.0, green: 212/255.0, blue: 212/255.0, alpha: 1.0), + UIColor(red: 136/255.0, green: 180/255.0, blue: 187/255.0, alpha: 1.0), + UIColor(red: 118/255.0, green: 174/255.0, blue: 175/255.0, alpha: 1.0), + UIColor(red: 42/255.0, green: 109/255.0, blue: 130/255.0, alpha: 1.0) + ]; + } + + public class func joyful () -> [UIColor] + { + return [ + UIColor(red: 217/255.0, green: 80/255.0, blue: 138/255.0, alpha: 1.0), + UIColor(red: 254/255.0, green: 149/255.0, blue: 7/255.0, alpha: 1.0), + UIColor(red: 254/255.0, green: 247/255.0, blue: 120/255.0, alpha: 1.0), + UIColor(red: 106/255.0, green: 167/255.0, blue: 134/255.0, alpha: 1.0), + UIColor(red: 53/255.0, green: 194/255.0, blue: 209/255.0, alpha: 1.0) + ]; + } + + public class func pastel () -> [UIColor] + { + return [ + UIColor(red: 64/255.0, green: 89/255.0, blue: 128/255.0, alpha: 1.0), + UIColor(red: 149/255.0, green: 165/255.0, blue: 124/255.0, alpha: 1.0), + UIColor(red: 217/255.0, green: 184/255.0, blue: 162/255.0, alpha: 1.0), + UIColor(red: 191/255.0, green: 134/255.0, blue: 134/255.0, alpha: 1.0), + UIColor(red: 179/255.0, green: 48/255.0, blue: 80/255.0, alpha: 1.0) + ]; + } + + public class func colorful () -> [UIColor] + { + return [ + UIColor(red: 193/255.0, green: 37/255.0, blue: 82/255.0, alpha: 1.0), + UIColor(red: 255/255.0, green: 102/255.0, blue: 0/255.0, alpha: 1.0), + UIColor(red: 245/255.0, green: 199/255.0, blue: 0/255.0, alpha: 1.0), + UIColor(red: 106/255.0, green: 150/255.0, blue: 31/255.0, alpha: 1.0), + UIColor(red: 179/255.0, green: 100/255.0, blue: 53/255.0, alpha: 1.0) + ]; + } + + public class func vordiplom () -> [UIColor] + { + return [ + UIColor(red: 192/255.0, green: 255/255.0, blue: 140/255.0, alpha: 1.0), + UIColor(red: 255/255.0, green: 247/255.0, blue: 140/255.0, alpha: 1.0), + UIColor(red: 255/255.0, green: 208/255.0, blue: 140/255.0, alpha: 1.0), + UIColor(red: 140/255.0, green: 234/255.0, blue: 255/255.0, alpha: 1.0), + UIColor(red: 255/255.0, green: 140/255.0, blue: 157/255.0, alpha: 1.0) + ]; + } +} \ No newline at end of file diff --git a/Charts/Classes/Utils/ChartFillFormatter.swift b/Charts/Classes/Utils/ChartFillFormatter.swift new file mode 100644 index 0000000000..85b1d77ec5 --- /dev/null +++ b/Charts/Classes/Utils/ChartFillFormatter.swift @@ -0,0 +1,22 @@ +// +// ChartFillFormatter.swift +// Charts +// +// Created by Daniel Cohen Gindi on 6/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// Protocol for providing a custom logic to where the filling line of a DataSet should end. If setFillEnabled(...) is set to true. +@objc +public protocol ChartFillFormatter +{ + /// Returns the vertical (y-axis) position where the filled-line of the DataSet should end. + func getFillLinePosition(#dataSet: LineChartDataSet, data: LineChartData, chartMaxY: Float, chartMinY: Float) -> CGFloat; +} diff --git a/Charts/Classes/Utils/ChartHighlight.swift b/Charts/Classes/Utils/ChartHighlight.swift new file mode 100644 index 0000000000..daf427e6ed --- /dev/null +++ b/Charts/Classes/Utils/ChartHighlight.swift @@ -0,0 +1,121 @@ +// +// ChartHighlight.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartHighlight: NSObject +{ + /// the x-index of the highlighted value + private var _xIndex = Int(0) + + /// the index of the dataset the highlighted value is in + private var _dataSetIndex = Int(0) + + /// index which value of a stacked bar entry is highlighted + /// :default: -1 + private var _stackIndex = Int(-1) + + public override init() + { + super.init(); + } + + public init(xIndex x: Int, dataSetIndex: Int) + { + super.init(); + + _xIndex = x; + _dataSetIndex = dataSetIndex; + } + + public init(xIndex x: Int, dataSetIndex: Int, stackIndex: Int) + { + super.init(); + + _xIndex = x; + _dataSetIndex = dataSetIndex; + _stackIndex = stackIndex; + } + + public var dataSetIndex: Int { return _dataSetIndex; } + public var xIndex: Int { return _xIndex; } + public var stackIndex: Int { return _stackIndex; } + + // MARK: NSObject + + public override var description: String + { + return "Highlight, xIndex: \(_xIndex), dataSetIndex: \(_dataSetIndex), stackIndex (only stacked barentry): \(_stackIndex)"; + } + + public override func isEqual(object: AnyObject?) -> Bool + { + if (object == nil) + { + return false; + } + + if (!object!.isKindOfClass(self.dynamicType)) + { + return false; + } + + if (object!.xIndex != _xIndex) + { + return false; + } + + if (object!.dataSetIndex != _dataSetIndex) + { + return false; + } + + if (object!.stackIndex != _stackIndex) + { + return false; + } + + return true; + } +} + +func ==(lhs: ChartHighlight, rhs: ChartHighlight) -> Bool +{ + if (lhs === rhs) + { + return true; + } + + if (!lhs.isKindOfClass(rhs.dynamicType)) + { + return false; + } + + if (lhs._xIndex != rhs._xIndex) + { + return false; + } + + if (lhs._dataSetIndex != rhs._dataSetIndex) + { + return false; + } + + if (lhs._stackIndex != rhs._stackIndex) + { + return false; + } + + return true; +} \ No newline at end of file diff --git a/Charts/Classes/Utils/ChartSelInfo.swift b/Charts/Classes/Utils/ChartSelInfo.swift new file mode 100644 index 0000000000..948b4a101b --- /dev/null +++ b/Charts/Classes/Utils/ChartSelInfo.swift @@ -0,0 +1,113 @@ +// +// ChartselInfo.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartSelInfo: NSObject +{ + private var _value = Float(0) + private var _dataSetIndex = Int(0) + private var _dataSet: ChartDataSet! + + public override init() + { + super.init(); + } + + public init(value: Float, dataSetIndex: Int, dataSet: ChartDataSet) + { + super.init(); + + _value = value; + _dataSetIndex = dataSetIndex; + _dataSet = dataSet; + } + + public var value: Float + { + return _value; + } + + public var dataSetIndex: Int + { + return _dataSetIndex; + } + + public var dataSet: ChartDataSet? + { + return _dataSet; + } + + // MARK: NSObject + + public override func isEqual(object: AnyObject?) -> Bool + { + if (object == nil) + { + return false; + } + + if (!object!.isKindOfClass(self.dynamicType)) + { + return false; + } + + if (object!.value != _value) + { + return false; + } + + if (object!.dataSetIndex != _dataSetIndex) + { + return false; + } + + if (object!.dataSet !== _dataSet) + { + return false; + } + + return true; + } +} + +public func ==(lhs: ChartSelInfo, rhs: ChartSelInfo) -> Bool +{ + if (lhs === rhs) + { + return true; + } + + if (!lhs.isKindOfClass(rhs.dynamicType)) + { + return false; + } + + if (lhs.value != rhs.value) + { + return false; + } + + if (lhs.dataSetIndex != rhs.dataSetIndex) + { + return false; + } + + if (lhs.dataSet !== rhs.dataSet) + { + return false; + } + + return true; +} \ No newline at end of file diff --git a/Charts/Classes/Utils/ChartTransformer.swift b/Charts/Classes/Utils/ChartTransformer.swift new file mode 100644 index 0000000000..4c328cf043 --- /dev/null +++ b/Charts/Classes/Utils/ChartTransformer.swift @@ -0,0 +1,269 @@ +// +// ChartTransformer.swift +// Charts +// +// Created by Daniel Cohen Gindi on 6/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +/// Transformer class that contains all matrices and is responsible for transforming values into pixels on the screen and backwards. +public class ChartTransformer: NSObject +{ + /// matrix to map the values to the screen pixels + internal var _matrixValueToPx = CGAffineTransformIdentity + + /// matrix for handling the different offsets of the chart + internal var _matrixOffset = CGAffineTransformIdentity + + private var _viewPortHandler: ChartViewPortHandler + + public init(viewPortHandler: ChartViewPortHandler) + { + _viewPortHandler = viewPortHandler; + } + + /// Prepares the matrix that transforms values to pixels. Calculates the scale factors from the charts size and offsets. + public func prepareMatrixValuePx(#chartXMin: Float, deltaX: CGFloat, deltaY: CGFloat, chartYMin: Float) + { + var scaleX = ((_viewPortHandler.chartWidth - _viewPortHandler.offsetRight - _viewPortHandler.offsetLeft) / deltaX); + var scaleY = ((_viewPortHandler.chartHeight - _viewPortHandler.offsetTop - _viewPortHandler.offsetBottom) / deltaY); + + // setup all matrices + _matrixValueToPx = CGAffineTransformIdentity; + _matrixValueToPx = CGAffineTransformScale(_matrixValueToPx, scaleX, -scaleY); + _matrixValueToPx = CGAffineTransformTranslate(_matrixValueToPx, CGFloat(-chartXMin), CGFloat(-chartYMin)); + } + + /// Prepares the matrix that contains all offsets. + public func prepareMatrixOffset(inverted: Bool) + { + if (!inverted) + { + _matrixOffset = CGAffineTransformTranslate(CGAffineTransformIdentity, _viewPortHandler.offsetLeft, _viewPortHandler.chartHeight - _viewPortHandler.offsetBottom); + } + else + { + _matrixOffset = CGAffineTransformScale(_matrixOffset, 1.0, -1.0); + _matrixOffset = CGAffineTransformTranslate(CGAffineTransformIdentity, _viewPortHandler.offsetLeft, -_viewPortHandler.offsetTop); + } + } + + /// Transforms an arraylist of Entry into a float array containing the x and y values transformed with all matrices for the SCATTERCHART. + public func generateTransformedValuesScatter(entries: [ChartDataEntry], phaseY: CGFloat) -> [CGPoint] + { + var valuePoints = [CGPoint](); + valuePoints.reserveCapacity(entries.count); + + for (var j = 0; j < entries.count; j++) + { + var e = entries[j]; + valuePoints.append(CGPoint(x: CGFloat(e.xIndex), y: CGFloat(e.value) * phaseY)); + } + + pointValuesToPixel(&valuePoints); + + return valuePoints; + } + + /// Transforms an arraylist of Entry into a float array containing the x and y values transformed with all matrices for the LINECHART. + public func generateTransformedValuesLine(entries: [ChartDataEntry], phaseY: CGFloat) -> [CGPoint] + { + var valuePoints = [CGPoint](); + valuePoints.reserveCapacity(entries.count); + + for (var j = 0; j < entries.count; j++) + { + var e = entries[j]; + valuePoints.append(CGPoint(x: CGFloat(e.xIndex), y: CGFloat(e.value) * phaseY)); + } + + pointValuesToPixel(&valuePoints); + + return valuePoints; + } + + /// Transforms an arraylist of Entry into a float array containing the x and y values transformed with all matrices for the CANDLESTICKCHART. + public func generateTransformedValuesCandle(entries: [CandleChartDataEntry], phaseY: CGFloat) -> [CGPoint] + { + var valuePoints = [CGPoint](); + valuePoints.reserveCapacity(entries.count); + + for (var j = 0; j < entries.count; j++) + { + var e = entries[j]; + valuePoints.append(CGPoint(x: CGFloat(e.xIndex), y: CGFloat(e.high) * phaseY)); + } + + pointValuesToPixel(&valuePoints); + + return valuePoints; + } + + /// Transforms an arraylist of Entry into a float array containing the x and y values transformed with all matrices for the BARCHART. + public func generateTransformedValuesBarChart(entries: [BarChartDataEntry], dataSet: Int, barData: BarChartData, phaseY: CGFloat) -> [CGPoint] + { + var valuePoints = [CGPoint](); + valuePoints.reserveCapacity(entries.count); + + var setCount = barData.dataSetCount; + var space = barData.groupSpace; + + for (var j = 0; j < entries.count; j++) + { + var e = entries[j]; + + // calculate the x-position, depending on datasetcount + var x = CGFloat(e.xIndex + (j * (setCount - 1)) + dataSet) + space * CGFloat(j) + space / 2.0; + var y = e.value; + + valuePoints.append(CGPoint(x: x, y: CGFloat(y) * phaseY)); + } + + pointValuesToPixel(&valuePoints); + + return valuePoints; + } + + /// Transforms an arraylist of Entry into a float array containing the x and y values transformed with all matrices for the BARCHART. + public func generateTransformedValuesHorizontalBarChart(entries: [ChartDataEntry], dataSet: Int, barData: BarChartData, phaseY: CGFloat) -> [CGPoint] + { + var valuePoints = [CGPoint](); + valuePoints.reserveCapacity(entries.count); + + var setCount = barData.dataSetCount; + var space = barData.groupSpace; + + for (var j = 0; j < entries.count; j++) + { + var e = entries[j]; + + // calculate the x-position, depending on datasetcount + var x = CGFloat(e.xIndex + (j * (setCount - 1)) + dataSet) + space * CGFloat(j) + space / 2.0; + var y = e.value; + + valuePoints.append(CGPoint(x: CGFloat(y) * phaseY, y: x)); + } + + pointValuesToPixel(&valuePoints); + + return valuePoints; + } + + /// Transform an array of points with all matrices. + // VERY IMPORTANT: Keep matrix order "value-touch-offset" when transforming. + public func pointValuesToPixel(inout pts: [CGPoint]) + { + var trans = valueToPixelMatrix; + for (var i = 0, count = pts.count; i < count; i++) + { + pts[i] = CGPointApplyAffineTransform(pts[i], trans); + } + } + + public func pointValueToPixel(inout point: CGPoint) + { + point = CGPointApplyAffineTransform(point, valueToPixelMatrix); + } + + /// Transform a rectangle with all matrices. + public func rectValueToPixel(inout r: CGRect) + { + r = CGRectApplyAffineTransform(r, valueToPixelMatrix); + } + + /// Transform a rectangle with all matrices with potential animation phases. + public func rectValueToPixel(inout r: CGRect, phaseY: CGFloat) + { + // multiply the height of the rect with the phase + if (r.origin.y > 0.0) + { + r.origin.y *= phaseY; + } + else + { + var bottom = r.origin.y + r.size.height; + bottom *= phaseY; + r.size.height = bottom - r.origin.y; + } + + r = CGRectApplyAffineTransform(r, valueToPixelMatrix); + } + + /// Transform a rectangle with all matrices with potential animation phases. + public func rectValueToPixelHorizontal(inout r: CGRect, phaseY: CGFloat) + { + // multiply the height of the rect with the phase + if (r.origin.x > 0.0) + { + r.origin.x *= phaseY; + } + else + { + var right = r.origin.x + r.size.width; + right *= phaseY; + r.size.width = right - r.origin.x; + } + + r = CGRectApplyAffineTransform(r, valueToPixelMatrix); + } + + /// transforms multiple rects with all matrices + public func rectValuesToPixel(inout rects: [CGRect]) + { + var trans = valueToPixelMatrix; + + for (var i = 0; i < rects.count; i++) + { + rects[i] = CGRectApplyAffineTransform(rects[i], trans); + } + } + + /// Transforms the given array of touch points (pixels) into values on the chart. + public func pixelsToValue(inout pixels: [CGPoint]) + { + var trans = pixelToValueMatrix; + + for (var i = 0; i < pixels.count; i++) + { + pixels[i] = CGPointApplyAffineTransform(pixels[i], trans); + } + } + + /// Transforms the given touch point (pixels) into a value on the chart. + public func pixelToValue(inout pixel: CGPoint) + { + pixel = CGPointApplyAffineTransform(pixel, pixelToValueMatrix); + } + + /// Returns the x and y values in the chart at the given touch point + /// (encapsulated in a PointD). This method transforms pixel coordinates to + /// coordinates / values in the chart. + public func getValueByTouchPoint(point: CGPoint) -> CGPoint + { + return CGPointApplyAffineTransform(point, pixelToValueMatrix); + } + + public var valueToPixelMatrix: CGAffineTransform + { + return + CGAffineTransformConcat( + CGAffineTransformConcat( + _matrixValueToPx, + _viewPortHandler.touchMatrix + ), + _matrixOffset + ); + } + + public var pixelToValueMatrix: CGAffineTransform + { + return CGAffineTransformInvert(valueToPixelMatrix); + } +} \ No newline at end of file diff --git a/Charts/Classes/Utils/ChartUtils.swift b/Charts/Classes/Utils/ChartUtils.swift new file mode 100644 index 0000000000..acdfe416d8 --- /dev/null +++ b/Charts/Classes/Utils/ChartUtils.swift @@ -0,0 +1,136 @@ +// +// Utils.swift +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit +import Darwin; + +internal class ChartUtils +{ + internal struct Math + { + internal static let FDEG2RAD = CGFloat(M_PI / 180.0); + internal static let FRAD2DEG = CGFloat(180.0 / M_PI); + internal static let DEG2RAD = M_PI / 180.0; + internal static let RAD2DEG = 180.0 / M_PI; + }; + + internal class func roundToNextSignificant(#number: Double) -> Double + { + if (isinf(number) || isnan(number) || number == 0) + { + return number; + } + + let d = ceil(log10(number < 0.0 ? -number : number)); + let pw = 1 - Int(d); + let magnitude = pow(Double(10.0), Double(pw)); + let shifted = round(number * magnitude); + return shifted / magnitude; + } + + internal class func decimals(number: Float) -> Int + { + if (number == 0.0) + { + return 0; + } + + var i = roundToNextSignificant(number: Double(number)); + return Int(ceil(-log10(i))) + 2; + } + + internal class func nextUp(number: Double) -> Double + { + if (isinf(number) || isnan(number)) + { + return number; + } + else + { + return number + DBL_EPSILON; + } + } + + /// Returns the index of the DataSet that contains the closest value on the y-axis. This is needed for highlighting. + internal class func closestDataSetIndex(valsAtIndex: [ChartSelInfo], value: Float, axis: ChartYAxis.AxisDependency?) -> Int + { + var index = -1; + var distance = FLT_MAX; + + for (var i = 0; i < valsAtIndex.count; i++) + { + var sel = valsAtIndex[i]; + + if (axis == nil || sel.dataSet?.axisDependency == axis) + { + var cdistance = abs(sel.value - value); + if (cdistance < distance) + { + index = valsAtIndex[i].dataSetIndex; + distance = cdistance; + } + } + } + + return index; + } + + /// Returns the minimum distance from a touch-y-value (in pixels) to the closest y-value (in pixels) that is displayed in the chart. + internal class func getMinimumDistance(valsAtIndex: [ChartSelInfo], val: Float, axis: ChartYAxis.AxisDependency) -> Float + { + var distance = FLT_MAX; + + for (var i = 0, count = valsAtIndex.count; i < count; i++) + { + var sel = valsAtIndex[i]; + + if (sel.dataSet!.axisDependency == axis) + { + var cdistance = abs(sel.value - val); + if (cdistance < distance) + { + distance = cdistance; + } + } + } + + return distance; + } + + /// Calculates the position around a center point, depending on the distance from the center, and the angle of the position around the center. + internal class func getPosition(#center: CGPoint, dist: CGFloat, angle: CGFloat) -> CGPoint + { + return CGPoint( + x: center.x + dist * cos(angle * Math.FDEG2RAD), + y: center.y + dist * sin(angle * Math.FDEG2RAD) + ); + } + + internal class func drawText(#context: CGContext, text: String, var point: CGPoint, align: NSTextAlignment, attributes: [NSObject : AnyObject]?) + { + if (align == .Center) + { + point.x -= text.sizeWithAttributes(attributes).width / 2.0; + } + else if (align == .Right) + { + point.x -= text.sizeWithAttributes(attributes).width; + } + + UIGraphicsPushContext(context); + (text as NSString).drawAtPoint(point, withAttributes: attributes); + UIGraphicsPopContext(); + } +} \ No newline at end of file diff --git a/Charts/Classes/Utils/ChartViewPortHandler.swift b/Charts/Classes/Utils/ChartViewPortHandler.swift new file mode 100644 index 0000000000..0a9a328649 --- /dev/null +++ b/Charts/Classes/Utils/ChartViewPortHandler.swift @@ -0,0 +1,377 @@ +// +// ChartViewPortHandler.swift +// Charts +// +// Created by Daniel Cohen Gindi on 27/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +public class ChartViewPortHandler +{ + /// matrix used for touch events + private var _touchMatrix = CGAffineTransformIdentity; + + /// this rectangle defines the area in which graph values can be drawn + private var _contentRect = CGRect(); + + private var _chartWidth = CGFloat(0.0); + private var _chartHeight = CGFloat(0.0); + + /// minimum scale value on the y-axis + private var _minScaleY = CGFloat(1.0); + + /// minimum scale value on the x-axis + private var _minScaleX = CGFloat(1.0); + + /// contains the current scale factor of the x-axis + private var _scaleX = CGFloat(1.0); + + /// contains the current scale factor of the y-axis + private var _scaleY = CGFloat(1.0); + + /// offset that allows the chart to be dragged over its bounds on the x-axis + private var _transOffsetX = CGFloat(0.0); + + /// offset that allows the chart to be dragged over its bounds on the x-axis + private var _transOffsetY = CGFloat(0.0); + + public init() + { + } + + public init(width: CGFloat, height: CGFloat) + { + _chartHeight = height; + _chartWidth = width; + } + + public func setChartDimens(#width: CGFloat, height: CGFloat) + { + _chartHeight = height; + _chartWidth = width; + + if (_contentRect.size.width <= 0.0 || _contentRect.size.height <= 0.0) + { + _contentRect.origin.x = 0.0; + _contentRect.origin.y = 0.0; + _contentRect.size.width = width; + _contentRect.size.height = height; + } + } + + public func restrainViewPort(#offsetLeft: CGFloat, offsetTop: CGFloat, offsetRight: CGFloat, offsetBottom: CGFloat) + { + _contentRect.origin.x = offsetLeft; + _contentRect.origin.y = offsetTop; + _contentRect.size.width = _chartWidth - offsetLeft - offsetRight; + _contentRect.size.height = _chartHeight - offsetBottom - offsetTop; + } + + public var offsetLeft: CGFloat + { + return _contentRect.origin.x; + } + + public var offsetRight: CGFloat + { + return _chartWidth - _contentRect.size.width - _contentRect.origin.x; + } + + public var offsetTop: CGFloat + { + return _contentRect.origin.y; + } + + public var offsetBottom: CGFloat + { + return _chartHeight - _contentRect.size.height - _contentRect.origin.y; + } + + public var contentTop: CGFloat + { + return _contentRect.origin.y; + } + + public var contentLeft: CGFloat + { + return _contentRect.origin.x; + } + + public var contentRight: CGFloat + { + return _contentRect.origin.x + _contentRect.size.width; + } + + public var contentBottom: CGFloat + { + return _contentRect.origin.y + _contentRect.size.height; + } + + public var contentWidth: CGFloat + { + return _contentRect.size.width; + } + + public var contentHeight: CGFloat + { + return _contentRect.size.height; + } + + public var contentRect: CGRect { return _contentRect; } + + public var contentCenter: CGPoint + { + return CGPoint(x: _contentRect.origin.x + _contentRect.size.width / 2.0, y: _contentRect.origin.y + _contentRect.size.height / 2.0); + } + + public var chartHeight: CGFloat { return _chartHeight; } + + public var chartWidth: CGFloat { return _chartWidth; } + + // MARK: - Scaling/Panning etc. + + /// Zooms around the specified center + public func zoom(#scaleX: CGFloat, scaleY: CGFloat, x: CGFloat, y: CGFloat) -> CGAffineTransform + { + var matrix = CGAffineTransformTranslate(_touchMatrix, x, y); + matrix = CGAffineTransformScale(matrix, scaleX, scaleY); + matrix = CGAffineTransformTranslate(matrix, -x, -y); + return matrix; + } + + /// Zooms in by 1.4f, x and y are the coordinates (in pixels) of the zoom center. + public func zoomIn(#x: CGFloat, y: CGFloat) -> CGAffineTransform + { + return zoom(scaleX: 1.4, scaleY: 1.4, x: x, y: y); + } + + /// Zooms out by 0.7f, x and y are the coordinates (in pixels) of the zoom center. + public func zoomOut(#x: CGFloat, y: CGFloat) -> CGAffineTransform + { + return zoom(scaleX: 0.7, scaleY: 0.7, x: x, y: y); + } + + /// Resets all zooming and dragging and makes the chart fit exactly it's bounds. + public func fitScreen() -> CGAffineTransform + { + return CGAffineTransformIdentity; + } + + /// Centers the viewport around the specified position (x-index and y-value) in the chart. + public func centerViewPort(#pt: CGPoint, chart: ChartViewBase) + { + var matrix = CGAffineTransformTranslate( + _touchMatrix, + -(pt.x - offsetLeft), + -(pt.y - offsetTop) + ); + + refresh(newMatrix: matrix, chart: chart, invalidate: false); + } + + /// call this method to refresh the graph with a given matrix + public func refresh(#newMatrix: CGAffineTransform, chart: ChartViewBase, invalidate: Bool) -> CGAffineTransform + { + _touchMatrix = newMatrix; + + // make sure scale and translation are within their bounds + limitTransAndScale(matrix: &_touchMatrix, content: _contentRect); + + chart.setNeedsDisplay(); + + return _touchMatrix; + } + + /// limits the maximum scale and X translation of the given matrix + private func limitTransAndScale(inout #matrix: CGAffineTransform, content: CGRect?) + { + // min scale-x is 1f + _scaleX = max(_minScaleX, matrix.a); + + // min scale-y is 1f + _scaleY = max(_minScaleY, matrix.d); + + var width: CGFloat = 0.0; + var height: CGFloat = 0.0; + + if (content != nil) + { + width = content!.width; + height = content!.height; + } + + var maxTransX = -width * (_scaleX - 1.0); + var newTransX = min(max(matrix.tx, maxTransX - _transOffsetX), _transOffsetX); + + var maxTransY = height * (_scaleY - 1.0); + var newTransY = max(min(matrix.ty, maxTransY + _transOffsetY), -_transOffsetY); + + matrix.tx = newTransX; + matrix.a = _scaleX; + matrix.ty = newTransY; + matrix.d = _scaleY; + } + + public func setMinimumScaleX(var xScale: CGFloat) + { + if (xScale < 1.0) + { + xScale = 1.0; + } + + _minScaleX = xScale; + + limitTransAndScale(matrix: &_touchMatrix, content: _contentRect); + } + + public func setMinimumScaleY(var yScale: CGFloat) + { + if (yScale < 1.0) + { + yScale = 1.0; + } + + _minScaleY = yScale; + + limitTransAndScale(matrix: &_touchMatrix, content: _contentRect); + } + + public var touchMatrix: CGAffineTransform + { + return _touchMatrix; + } + + // MARK: - Boundaries Check + + public func isInBoundsX(x: CGFloat) -> Bool + { + if (isInBoundsLeft(x) && isInBoundsRight(x)) + { + return true; + } + else + { + return false; + } + } + + public func isInBoundsY(y: CGFloat) -> Bool + { + if (isInBoundsTop(y) && isInBoundsBottom(y)) + { + return true; + } + else + { + return false; + } + } + + public func isInBounds(#x: CGFloat, y: CGFloat) -> Bool + { + if (isInBoundsX(x) && isInBoundsY(y)) + { + return true; + } + else + { + return false; + } + } + + public func isInBoundsLeft(x: CGFloat) -> Bool + { + return _contentRect.origin.x <= x ? true : false; + } + + public func isInBoundsRight(x: CGFloat) -> Bool + { + return (_contentRect.origin.x + _contentRect.size.width) >= x ? true : false; + } + + public func isInBoundsTop(y: CGFloat) -> Bool + { + return _contentRect.origin.y <= y ? true : false; + } + + public func isInBoundsBottom(y: CGFloat) -> Bool + { + return (_contentRect.origin.y + _contentRect.size.height) >= y ? true : false; + } + + /// returns the current x-scale factor + public var scaleX: CGFloat + { + return _scaleX; + } + + /// returns the current y-scale factor + public var scaleY: CGFloat + { + return _scaleY; + } + + /// if the chart is fully zoomed out, return true + public var isFullyZoomedOut: Bool + { + if (isFullyZoomedOutX && isFullyZoomedOutY) + { + return true; + } + else + { + return false; + } + } + + /// Returns true if the chart is fully zoomed out on it's y-axis (vertical). + public var isFullyZoomedOutY: Bool + { + if (_scaleY > _minScaleY || _minScaleY > 1.0) + { + return false; + } + else + { + return true; + } + } + + /// Returns true if the chart is fully zoomed out on it's x-axis (horizontal). + public var isFullyZoomedOutX: Bool + { + if (_scaleX > _minScaleX || _minScaleX > 1.0) + { + return false; + } + else + { + return true; + } + } + + /// Set an offset in pixels that allows the user to drag the chart over it's bounds on the x-axis. + public func setDragOffsetX(offset: CGFloat) + { + _transOffsetX = offset; + } + + /// Set an offset in pixels that allows the user to drag the chart over it's bounds on the y-axis. + public func setDragOffsetY(offset: CGFloat) + { + _transOffsetY = offset; + } + + /// Returns true if both drag offsets (x and y) are zero or smaller. + public var hasNoDragOffset: Bool + { + return _transOffsetX <= 0.0 && _transOffsetY <= 0.0 ? true : false; + } +} \ No newline at end of file diff --git a/Charts/Supporting Files/Charts.h b/Charts/Supporting Files/Charts.h new file mode 100755 index 0000000000..eb089f6837 --- /dev/null +++ b/Charts/Supporting Files/Charts.h @@ -0,0 +1,25 @@ +// +// Charts.h +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import + +//! Project version number for Charts. +FOUNDATION_EXPORT double ChartsVersionNumber; + +//! Project version string for Charts. +FOUNDATION_EXPORT const unsigned char ChartsVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Charts/Supporting Files/Info.plist b/Charts/Supporting Files/Info.plist new file mode 100644 index 0000000000..4b3eb0f2dc --- /dev/null +++ b/Charts/Supporting Files/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.dcg.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/ChartsDemo/ChartsDemo.xcodeproj/project.pbxproj b/ChartsDemo/ChartsDemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..842f943663 --- /dev/null +++ b/ChartsDemo/ChartsDemo.xcodeproj/project.pbxproj @@ -0,0 +1,582 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5B0CC7851ABB875400665592 /* PieChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B0CC7831ABB875400665592 /* PieChartViewController.m */; }; + 5B0CC7861ABB875400665592 /* PieChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B0CC7841ABB875400665592 /* PieChartViewController.xib */; }; + 5B4316271AB8D8AE0009FCAA /* Icon-29@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B43161F1AB8D8AE0009FCAA /* Icon-29@2x.png */; }; + 5B4316281AB8D8AE0009FCAA /* Icon-29@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B4316201AB8D8AE0009FCAA /* Icon-29@3x.png */; }; + 5B4316291AB8D8AE0009FCAA /* Icon-40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B4316211AB8D8AE0009FCAA /* Icon-40@2x.png */; }; + 5B43162A1AB8D8AE0009FCAA /* Icon-40@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B4316221AB8D8AE0009FCAA /* Icon-40@3x.png */; }; + 5B43162B1AB8D8AE0009FCAA /* Icon-60@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B4316231AB8D8AE0009FCAA /* Icon-60@2x.png */; }; + 5B43162C1AB8D8AE0009FCAA /* Icon-60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B4316241AB8D8AE0009FCAA /* Icon-60@3x.png */; }; + 5B43162D1AB8D8AE0009FCAA /* iTunesArtwork in Resources */ = {isa = PBXBuildFile; fileRef = 5B4316251AB8D8AE0009FCAA /* iTunesArtwork */; }; + 5B43162E1AB8D8AE0009FCAA /* iTunesArtwork@2x in Resources */ = {isa = PBXBuildFile; fileRef = 5B4316261AB8D8AE0009FCAA /* iTunesArtwork@2x */; }; + 5B4316351AB8D8B70009FCAA /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B4316311AB8D8B70009FCAA /* Default-568h@2x.png */; }; + 5B4316361AB8D8B70009FCAA /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B4316321AB8D8B70009FCAA /* Default-667h@2x.png */; }; + 5B4316371AB8D8B70009FCAA /* Default-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B4316331AB8D8B70009FCAA /* Default-736h@3x.png */; }; + 5B4316381AB8D8B70009FCAA /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 5B4316341AB8D8B70009FCAA /* Default@2x.png */; }; + 5B57BBB51A9B26AA0036A6CC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B57BBB41A9B26AA0036A6CC /* main.m */; }; + 5B57BBB81A9B26AA0036A6CC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B57BBB71A9B26AA0036A6CC /* AppDelegate.m */; }; + 5B57BBBB1A9B26AA0036A6CC /* DemoListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B57BBBA1A9B26AA0036A6CC /* DemoListViewController.m */; }; + 5B8EAF241AB3271B009697AA /* DemoListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B8EAF231AB3271B009697AA /* DemoListViewController.xib */; }; + 5B8EAF281AB32CF5009697AA /* DemoBaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B8EAF261AB32CF5009697AA /* DemoBaseViewController.m */; }; + 5B8EAF301AB32E15009697AA /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5B8EAF2F1AB32E15009697AA /* Images.xcassets */; }; + 5B8EAF3D1AB32F27009697AA /* Charts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B8EAF371AB32EA1009697AA /* Charts.framework */; }; + 5BD47E5B1ABB0263008FCEC6 /* BalloonMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD47E5A1ABB0263008FCEC6 /* BalloonMarker.swift */; }; + 5BD47E601ABB3C91008FCEC6 /* LineChart2ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BD47E5E1ABB3C91008FCEC6 /* LineChart2ViewController.m */; }; + 5BD47E611ABB3C91008FCEC6 /* LineChart2ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BD47E5F1ABB3C91008FCEC6 /* LineChart2ViewController.xib */; }; + 5BD47E651ABB424E008FCEC6 /* BarChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BD47E631ABB424E008FCEC6 /* BarChartViewController.m */; }; + 5BD47E661ABB424E008FCEC6 /* BarChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BD47E641ABB424E008FCEC6 /* BarChartViewController.xib */; }; + 5BD8F0741AB89CE500566E05 /* LineChart1ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BD8F0721AB89CE500566E05 /* LineChart1ViewController.m */; }; + 5BD8F0751AB89CE500566E05 /* LineChart1ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BD8F0731AB89CE500566E05 /* LineChart1ViewController.xib */; }; + 5BDEDC411ABB7F73007D3A60 /* HorizontalBarChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BDEDC3F1ABB7F73007D3A60 /* HorizontalBarChartViewController.m */; }; + 5BDEDC421ABB7F73007D3A60 /* HorizontalBarChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BDEDC401ABB7F73007D3A60 /* HorizontalBarChartViewController.xib */; }; + 5BDEDC471ABB871E007D3A60 /* CombinedChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BDEDC451ABB871E007D3A60 /* CombinedChartViewController.m */; }; + 5BDEDC481ABB871E007D3A60 /* CombinedChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BDEDC461ABB871E007D3A60 /* CombinedChartViewController.xib */; }; + 5BEAED121ABBFB2B0013F194 /* AnotherBarChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEAED101ABBFB2B0013F194 /* AnotherBarChartViewController.m */; }; + 5BEAED131ABBFB2B0013F194 /* AnotherBarChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BEAED111ABBFB2B0013F194 /* AnotherBarChartViewController.xib */; }; + 5BEAED1B1ABBFB340013F194 /* ScatterChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEAED161ABBFB340013F194 /* ScatterChartViewController.m */; }; + 5BEAED1C1ABBFB340013F194 /* ScatterChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BEAED171ABBFB340013F194 /* ScatterChartViewController.xib */; }; + 5BEAED1D1ABBFB340013F194 /* StackedBarChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEAED191ABBFB340013F194 /* StackedBarChartViewController.m */; }; + 5BEAED1E1ABBFB340013F194 /* StackedBarChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BEAED1A1ABBFB340013F194 /* StackedBarChartViewController.xib */; }; + 5BEAED251ABC0BE20013F194 /* MultipleBarChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEAED201ABC0BE20013F194 /* MultipleBarChartViewController.m */; }; + 5BEAED261ABC0BE20013F194 /* MultipleBarChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BEAED211ABC0BE20013F194 /* MultipleBarChartViewController.xib */; }; + 5BEAED271ABC0BE20013F194 /* MultipleLinesChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEAED231ABC0BE20013F194 /* MultipleLinesChartViewController.m */; }; + 5BEAED281ABC0BE20013F194 /* MultipleLinesChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BEAED241ABC0BE20013F194 /* MultipleLinesChartViewController.xib */; }; + 5BEAED2C1ABC160F0013F194 /* CandleStickChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEAED2A1ABC160F0013F194 /* CandleStickChartViewController.m */; }; + 5BEAED2D1ABC160F0013F194 /* CandleStickChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BEAED2B1ABC160F0013F194 /* CandleStickChartViewController.xib */; }; + 5BEAED311ABC18F00013F194 /* CubicLineChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEAED2F1ABC18F00013F194 /* CubicLineChartViewController.m */; }; + 5BEAED321ABC18F00013F194 /* CubicLineChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BEAED301ABC18F00013F194 /* CubicLineChartViewController.xib */; }; + 5BEAED361ABC192F0013F194 /* RadarChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEAED341ABC192F0013F194 /* RadarChartViewController.m */; }; + 5BEAED371ABC192F0013F194 /* RadarChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BEAED351ABC192F0013F194 /* RadarChartViewController.xib */; }; + 5BEAED3B1ABC199F0013F194 /* ColoredLineChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEAED391ABC199F0013F194 /* ColoredLineChartViewController.m */; }; + 5BEAED3C1ABC199F0013F194 /* ColoredLineChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BEAED3A1ABC199F0013F194 /* ColoredLineChartViewController.xib */; }; + 5BEAED401ABC1AC60013F194 /* SinusBarChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEAED3E1ABC1AC60013F194 /* SinusBarChartViewController.m */; }; + 5BEAED411ABC1AC60013F194 /* SinusBarChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BEAED3F1ABC1AC60013F194 /* SinusBarChartViewController.xib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 5B8EAF361AB32EA1009697AA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5B8EAF321AB32EA0009697AA /* Charts.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 5BA8EC401A9D14DC00CE82E1; + remoteInfo = Charts; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 5B0CC7821ABB875400665592 /* PieChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PieChartViewController.h; sourceTree = ""; }; + 5B0CC7831ABB875400665592 /* PieChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PieChartViewController.m; sourceTree = ""; }; + 5B0CC7841ABB875400665592 /* PieChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PieChartViewController.xib; sourceTree = ""; }; + 5B43161F1AB8D8AE0009FCAA /* Icon-29@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-29@2x.png"; sourceTree = ""; }; + 5B4316201AB8D8AE0009FCAA /* Icon-29@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-29@3x.png"; sourceTree = ""; }; + 5B4316211AB8D8AE0009FCAA /* Icon-40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-40@2x.png"; sourceTree = ""; }; + 5B4316221AB8D8AE0009FCAA /* Icon-40@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-40@3x.png"; sourceTree = ""; }; + 5B4316231AB8D8AE0009FCAA /* Icon-60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@2x.png"; sourceTree = ""; }; + 5B4316241AB8D8AE0009FCAA /* Icon-60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Icon-60@3x.png"; sourceTree = ""; }; + 5B4316251AB8D8AE0009FCAA /* iTunesArtwork */ = {isa = PBXFileReference; lastKnownFileType = file; path = iTunesArtwork; sourceTree = ""; }; + 5B4316261AB8D8AE0009FCAA /* iTunesArtwork@2x */ = {isa = PBXFileReference; lastKnownFileType = file; path = "iTunesArtwork@2x"; sourceTree = ""; }; + 5B4316311AB8D8B70009FCAA /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; + 5B4316321AB8D8B70009FCAA /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = ""; }; + 5B4316331AB8D8B70009FCAA /* Default-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h@3x.png"; sourceTree = ""; }; + 5B4316341AB8D8B70009FCAA /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; + 5B57BBAF1A9B26AA0036A6CC /* ChartsDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ChartsDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5B57BBB31A9B26AA0036A6CC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5B57BBB41A9B26AA0036A6CC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 5B57BBB61A9B26AA0036A6CC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 5B57BBB71A9B26AA0036A6CC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 5B57BBB91A9B26AA0036A6CC /* DemoListViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoListViewController.h; sourceTree = ""; }; + 5B57BBBA1A9B26AA0036A6CC /* DemoListViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoListViewController.m; sourceTree = ""; }; + 5B8EAF231AB3271B009697AA /* DemoListViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DemoListViewController.xib; sourceTree = ""; }; + 5B8EAF251AB32CF5009697AA /* DemoBaseViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoBaseViewController.h; sourceTree = ""; }; + 5B8EAF261AB32CF5009697AA /* DemoBaseViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoBaseViewController.m; sourceTree = ""; }; + 5B8EAF2F1AB32E15009697AA /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + 5B8EAF321AB32EA0009697AA /* Charts.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Charts.xcodeproj; path = ../Charts/Charts.xcodeproj; sourceTree = ""; }; + 5BD47E5A1ABB0263008FCEC6 /* BalloonMarker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BalloonMarker.swift; sourceTree = ""; }; + 5BD47E5C1ABB0273008FCEC6 /* ChartsDemo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ChartsDemo-Bridging-Header.h"; sourceTree = ""; }; + 5BD47E5D1ABB3C91008FCEC6 /* LineChart2ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LineChart2ViewController.h; sourceTree = ""; }; + 5BD47E5E1ABB3C91008FCEC6 /* LineChart2ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LineChart2ViewController.m; sourceTree = ""; }; + 5BD47E5F1ABB3C91008FCEC6 /* LineChart2ViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LineChart2ViewController.xib; sourceTree = ""; }; + 5BD47E621ABB424E008FCEC6 /* BarChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BarChartViewController.h; sourceTree = ""; }; + 5BD47E631ABB424E008FCEC6 /* BarChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BarChartViewController.m; sourceTree = ""; }; + 5BD47E641ABB424E008FCEC6 /* BarChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BarChartViewController.xib; sourceTree = ""; }; + 5BD8F0711AB89CE500566E05 /* LineChart1ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LineChart1ViewController.h; sourceTree = ""; }; + 5BD8F0721AB89CE500566E05 /* LineChart1ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LineChart1ViewController.m; sourceTree = ""; }; + 5BD8F0731AB89CE500566E05 /* LineChart1ViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LineChart1ViewController.xib; sourceTree = ""; }; + 5BDEDC3E1ABB7F73007D3A60 /* HorizontalBarChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HorizontalBarChartViewController.h; sourceTree = ""; }; + 5BDEDC3F1ABB7F73007D3A60 /* HorizontalBarChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HorizontalBarChartViewController.m; sourceTree = ""; }; + 5BDEDC401ABB7F73007D3A60 /* HorizontalBarChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HorizontalBarChartViewController.xib; sourceTree = ""; }; + 5BDEDC441ABB871E007D3A60 /* CombinedChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CombinedChartViewController.h; sourceTree = ""; }; + 5BDEDC451ABB871E007D3A60 /* CombinedChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CombinedChartViewController.m; sourceTree = ""; }; + 5BDEDC461ABB871E007D3A60 /* CombinedChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CombinedChartViewController.xib; sourceTree = ""; }; + 5BEAED0F1ABBFB2B0013F194 /* AnotherBarChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AnotherBarChartViewController.h; sourceTree = ""; }; + 5BEAED101ABBFB2B0013F194 /* AnotherBarChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AnotherBarChartViewController.m; sourceTree = ""; }; + 5BEAED111ABBFB2B0013F194 /* AnotherBarChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AnotherBarChartViewController.xib; sourceTree = ""; }; + 5BEAED151ABBFB340013F194 /* ScatterChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScatterChartViewController.h; sourceTree = ""; }; + 5BEAED161ABBFB340013F194 /* ScatterChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScatterChartViewController.m; sourceTree = ""; }; + 5BEAED171ABBFB340013F194 /* ScatterChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ScatterChartViewController.xib; sourceTree = ""; }; + 5BEAED181ABBFB340013F194 /* StackedBarChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StackedBarChartViewController.h; sourceTree = ""; }; + 5BEAED191ABBFB340013F194 /* StackedBarChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StackedBarChartViewController.m; sourceTree = ""; }; + 5BEAED1A1ABBFB340013F194 /* StackedBarChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StackedBarChartViewController.xib; sourceTree = ""; }; + 5BEAED1F1ABC0BE20013F194 /* MultipleBarChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultipleBarChartViewController.h; sourceTree = ""; }; + 5BEAED201ABC0BE20013F194 /* MultipleBarChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultipleBarChartViewController.m; sourceTree = ""; }; + 5BEAED211ABC0BE20013F194 /* MultipleBarChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MultipleBarChartViewController.xib; sourceTree = ""; }; + 5BEAED221ABC0BE20013F194 /* MultipleLinesChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultipleLinesChartViewController.h; sourceTree = ""; }; + 5BEAED231ABC0BE20013F194 /* MultipleLinesChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultipleLinesChartViewController.m; sourceTree = ""; }; + 5BEAED241ABC0BE20013F194 /* MultipleLinesChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MultipleLinesChartViewController.xib; sourceTree = ""; }; + 5BEAED291ABC160F0013F194 /* CandleStickChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CandleStickChartViewController.h; sourceTree = ""; }; + 5BEAED2A1ABC160F0013F194 /* CandleStickChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CandleStickChartViewController.m; sourceTree = ""; }; + 5BEAED2B1ABC160F0013F194 /* CandleStickChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CandleStickChartViewController.xib; sourceTree = ""; }; + 5BEAED2E1ABC18F00013F194 /* CubicLineChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CubicLineChartViewController.h; sourceTree = ""; }; + 5BEAED2F1ABC18F00013F194 /* CubicLineChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CubicLineChartViewController.m; sourceTree = ""; }; + 5BEAED301ABC18F00013F194 /* CubicLineChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CubicLineChartViewController.xib; sourceTree = ""; }; + 5BEAED331ABC192F0013F194 /* RadarChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RadarChartViewController.h; sourceTree = ""; }; + 5BEAED341ABC192F0013F194 /* RadarChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RadarChartViewController.m; sourceTree = ""; }; + 5BEAED351ABC192F0013F194 /* RadarChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RadarChartViewController.xib; sourceTree = ""; }; + 5BEAED381ABC199F0013F194 /* ColoredLineChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColoredLineChartViewController.h; sourceTree = ""; }; + 5BEAED391ABC199F0013F194 /* ColoredLineChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ColoredLineChartViewController.m; sourceTree = ""; }; + 5BEAED3A1ABC199F0013F194 /* ColoredLineChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ColoredLineChartViewController.xib; sourceTree = ""; }; + 5BEAED3D1ABC1AC60013F194 /* SinusBarChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SinusBarChartViewController.h; sourceTree = ""; }; + 5BEAED3E1ABC1AC60013F194 /* SinusBarChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SinusBarChartViewController.m; sourceTree = ""; }; + 5BEAED3F1ABC1AC60013F194 /* SinusBarChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SinusBarChartViewController.xib; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5B57BBAC1A9B26AA0036A6CC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5B8EAF3D1AB32F27009697AA /* Charts.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5B43161E1AB8D8AE0009FCAA /* app-icon */ = { + isa = PBXGroup; + children = ( + 5B43161F1AB8D8AE0009FCAA /* Icon-29@2x.png */, + 5B4316201AB8D8AE0009FCAA /* Icon-29@3x.png */, + 5B4316211AB8D8AE0009FCAA /* Icon-40@2x.png */, + 5B4316221AB8D8AE0009FCAA /* Icon-40@3x.png */, + 5B4316231AB8D8AE0009FCAA /* Icon-60@2x.png */, + 5B4316241AB8D8AE0009FCAA /* Icon-60@3x.png */, + 5B4316251AB8D8AE0009FCAA /* iTunesArtwork */, + 5B4316261AB8D8AE0009FCAA /* iTunesArtwork@2x */, + ); + path = "app-icon"; + sourceTree = ""; + }; + 5B4316301AB8D8B70009FCAA /* launch-image */ = { + isa = PBXGroup; + children = ( + 5B4316311AB8D8B70009FCAA /* Default-568h@2x.png */, + 5B4316321AB8D8B70009FCAA /* Default-667h@2x.png */, + 5B4316331AB8D8B70009FCAA /* Default-736h@3x.png */, + 5B4316341AB8D8B70009FCAA /* Default@2x.png */, + ); + path = "launch-image"; + sourceTree = ""; + }; + 5B57BBA61A9B26AA0036A6CC = { + isa = PBXGroup; + children = ( + 5B57BBB11A9B26AA0036A6CC /* Classes */, + 5B8EAF2E1AB32E15009697AA /* Resources */, + 5B57BBB21A9B26AA0036A6CC /* Supporting Files */, + 5B8EAF321AB32EA0009697AA /* Charts.xcodeproj */, + 5B57BBB01A9B26AA0036A6CC /* Products */, + ); + sourceTree = ""; + }; + 5B57BBB01A9B26AA0036A6CC /* Products */ = { + isa = PBXGroup; + children = ( + 5B57BBAF1A9B26AA0036A6CC /* ChartsDemo.app */, + ); + name = Products; + sourceTree = ""; + }; + 5B57BBB11A9B26AA0036A6CC /* Classes */ = { + isa = PBXGroup; + children = ( + 5B57BBB61A9B26AA0036A6CC /* AppDelegate.h */, + 5B57BBB71A9B26AA0036A6CC /* AppDelegate.m */, + 5B57BBB91A9B26AA0036A6CC /* DemoListViewController.h */, + 5B57BBBA1A9B26AA0036A6CC /* DemoListViewController.m */, + 5B8EAF231AB3271B009697AA /* DemoListViewController.xib */, + 5B8EAF251AB32CF5009697AA /* DemoBaseViewController.h */, + 5B8EAF261AB32CF5009697AA /* DemoBaseViewController.m */, + 5BD47E541ABB0177008FCEC6 /* Components */, + 5BD8F06F1AB89C7100566E05 /* Demos */, + ); + path = Classes; + sourceTree = ""; + }; + 5B57BBB21A9B26AA0036A6CC /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 5B57BBB31A9B26AA0036A6CC /* Info.plist */, + 5B57BBB41A9B26AA0036A6CC /* main.m */, + 5BD47E5C1ABB0273008FCEC6 /* ChartsDemo-Bridging-Header.h */, + ); + path = "Supporting Files"; + sourceTree = ""; + }; + 5B8EAF2E1AB32E15009697AA /* Resources */ = { + isa = PBXGroup; + children = ( + 5B8EAF2F1AB32E15009697AA /* Images.xcassets */, + 5B43161E1AB8D8AE0009FCAA /* app-icon */, + 5B4316301AB8D8B70009FCAA /* launch-image */, + ); + path = Resources; + sourceTree = ""; + }; + 5B8EAF331AB32EA0009697AA /* Products */ = { + isa = PBXGroup; + children = ( + 5B8EAF371AB32EA1009697AA /* Charts.framework */, + ); + name = Products; + sourceTree = ""; + }; + 5BD47E541ABB0177008FCEC6 /* Components */ = { + isa = PBXGroup; + children = ( + 5BD47E5A1ABB0263008FCEC6 /* BalloonMarker.swift */, + ); + path = Components; + sourceTree = ""; + }; + 5BD8F06F1AB89C7100566E05 /* Demos */ = { + isa = PBXGroup; + children = ( + 5BEAED0F1ABBFB2B0013F194 /* AnotherBarChartViewController.h */, + 5BEAED101ABBFB2B0013F194 /* AnotherBarChartViewController.m */, + 5BEAED111ABBFB2B0013F194 /* AnotherBarChartViewController.xib */, + 5BD47E621ABB424E008FCEC6 /* BarChartViewController.h */, + 5BD47E631ABB424E008FCEC6 /* BarChartViewController.m */, + 5BD47E641ABB424E008FCEC6 /* BarChartViewController.xib */, + 5BEAED291ABC160F0013F194 /* CandleStickChartViewController.h */, + 5BEAED2A1ABC160F0013F194 /* CandleStickChartViewController.m */, + 5BEAED2B1ABC160F0013F194 /* CandleStickChartViewController.xib */, + 5BEAED381ABC199F0013F194 /* ColoredLineChartViewController.h */, + 5BEAED391ABC199F0013F194 /* ColoredLineChartViewController.m */, + 5BEAED3A1ABC199F0013F194 /* ColoredLineChartViewController.xib */, + 5BDEDC441ABB871E007D3A60 /* CombinedChartViewController.h */, + 5BDEDC451ABB871E007D3A60 /* CombinedChartViewController.m */, + 5BDEDC461ABB871E007D3A60 /* CombinedChartViewController.xib */, + 5BEAED2E1ABC18F00013F194 /* CubicLineChartViewController.h */, + 5BEAED2F1ABC18F00013F194 /* CubicLineChartViewController.m */, + 5BEAED301ABC18F00013F194 /* CubicLineChartViewController.xib */, + 5BDEDC3E1ABB7F73007D3A60 /* HorizontalBarChartViewController.h */, + 5BDEDC3F1ABB7F73007D3A60 /* HorizontalBarChartViewController.m */, + 5BDEDC401ABB7F73007D3A60 /* HorizontalBarChartViewController.xib */, + 5BD8F0711AB89CE500566E05 /* LineChart1ViewController.h */, + 5BD8F0721AB89CE500566E05 /* LineChart1ViewController.m */, + 5BD8F0731AB89CE500566E05 /* LineChart1ViewController.xib */, + 5BD47E5D1ABB3C91008FCEC6 /* LineChart2ViewController.h */, + 5BD47E5E1ABB3C91008FCEC6 /* LineChart2ViewController.m */, + 5BD47E5F1ABB3C91008FCEC6 /* LineChart2ViewController.xib */, + 5BEAED1F1ABC0BE20013F194 /* MultipleBarChartViewController.h */, + 5BEAED201ABC0BE20013F194 /* MultipleBarChartViewController.m */, + 5BEAED211ABC0BE20013F194 /* MultipleBarChartViewController.xib */, + 5BEAED221ABC0BE20013F194 /* MultipleLinesChartViewController.h */, + 5BEAED231ABC0BE20013F194 /* MultipleLinesChartViewController.m */, + 5BEAED241ABC0BE20013F194 /* MultipleLinesChartViewController.xib */, + 5B0CC7821ABB875400665592 /* PieChartViewController.h */, + 5B0CC7831ABB875400665592 /* PieChartViewController.m */, + 5B0CC7841ABB875400665592 /* PieChartViewController.xib */, + 5BEAED331ABC192F0013F194 /* RadarChartViewController.h */, + 5BEAED341ABC192F0013F194 /* RadarChartViewController.m */, + 5BEAED351ABC192F0013F194 /* RadarChartViewController.xib */, + 5BEAED151ABBFB340013F194 /* ScatterChartViewController.h */, + 5BEAED161ABBFB340013F194 /* ScatterChartViewController.m */, + 5BEAED171ABBFB340013F194 /* ScatterChartViewController.xib */, + 5BEAED3D1ABC1AC60013F194 /* SinusBarChartViewController.h */, + 5BEAED3E1ABC1AC60013F194 /* SinusBarChartViewController.m */, + 5BEAED3F1ABC1AC60013F194 /* SinusBarChartViewController.xib */, + 5BEAED181ABBFB340013F194 /* StackedBarChartViewController.h */, + 5BEAED191ABBFB340013F194 /* StackedBarChartViewController.m */, + 5BEAED1A1ABBFB340013F194 /* StackedBarChartViewController.xib */, + ); + path = Demos; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 5B57BBAE1A9B26AA0036A6CC /* ChartsDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5B57BBD21A9B26AA0036A6CC /* Build configuration list for PBXNativeTarget "ChartsDemo" */; + buildPhases = ( + 5B57BBAB1A9B26AA0036A6CC /* Sources */, + 5B57BBAC1A9B26AA0036A6CC /* Frameworks */, + 5B57BBAD1A9B26AA0036A6CC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ChartsDemo; + productName = chartstest; + productReference = 5B57BBAF1A9B26AA0036A6CC /* ChartsDemo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5B57BBA71A9B26AA0036A6CC /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = dcg; + TargetAttributes = { + 5B57BBAE1A9B26AA0036A6CC = { + CreatedOnToolsVersion = 6.1.1; + }; + }; + }; + buildConfigurationList = 5B57BBAA1A9B26AA0036A6CC /* Build configuration list for PBXProject "ChartsDemo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5B57BBA61A9B26AA0036A6CC; + productRefGroup = 5B57BBB01A9B26AA0036A6CC /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 5B8EAF331AB32EA0009697AA /* Products */; + ProjectRef = 5B8EAF321AB32EA0009697AA /* Charts.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 5B57BBAE1A9B26AA0036A6CC /* ChartsDemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 5B8EAF371AB32EA1009697AA /* Charts.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Charts.framework; + remoteRef = 5B8EAF361AB32EA1009697AA /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 5B57BBAD1A9B26AA0036A6CC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5B8EAF301AB32E15009697AA /* Images.xcassets in Resources */, + 5B43162B1AB8D8AE0009FCAA /* Icon-60@2x.png in Resources */, + 5B43162E1AB8D8AE0009FCAA /* iTunesArtwork@2x in Resources */, + 5B4316281AB8D8AE0009FCAA /* Icon-29@3x.png in Resources */, + 5B4316361AB8D8B70009FCAA /* Default-667h@2x.png in Resources */, + 5BEAED2D1ABC160F0013F194 /* CandleStickChartViewController.xib in Resources */, + 5BD47E611ABB3C91008FCEC6 /* LineChart2ViewController.xib in Resources */, + 5BEAED131ABBFB2B0013F194 /* AnotherBarChartViewController.xib in Resources */, + 5BEAED411ABC1AC60013F194 /* SinusBarChartViewController.xib in Resources */, + 5BEAED371ABC192F0013F194 /* RadarChartViewController.xib in Resources */, + 5B4316351AB8D8B70009FCAA /* Default-568h@2x.png in Resources */, + 5B8EAF241AB3271B009697AA /* DemoListViewController.xib in Resources */, + 5BEAED261ABC0BE20013F194 /* MultipleBarChartViewController.xib in Resources */, + 5B43162A1AB8D8AE0009FCAA /* Icon-40@3x.png in Resources */, + 5BEAED3C1ABC199F0013F194 /* ColoredLineChartViewController.xib in Resources */, + 5BEAED321ABC18F00013F194 /* CubicLineChartViewController.xib in Resources */, + 5BEAED281ABC0BE20013F194 /* MultipleLinesChartViewController.xib in Resources */, + 5B4316381AB8D8B70009FCAA /* Default@2x.png in Resources */, + 5B0CC7861ABB875400665592 /* PieChartViewController.xib in Resources */, + 5BEAED1C1ABBFB340013F194 /* ScatterChartViewController.xib in Resources */, + 5BD8F0751AB89CE500566E05 /* LineChart1ViewController.xib in Resources */, + 5B43162D1AB8D8AE0009FCAA /* iTunesArtwork in Resources */, + 5BD47E661ABB424E008FCEC6 /* BarChartViewController.xib in Resources */, + 5BDEDC421ABB7F73007D3A60 /* HorizontalBarChartViewController.xib in Resources */, + 5BDEDC481ABB871E007D3A60 /* CombinedChartViewController.xib in Resources */, + 5B4316371AB8D8B70009FCAA /* Default-736h@3x.png in Resources */, + 5B4316291AB8D8AE0009FCAA /* Icon-40@2x.png in Resources */, + 5BEAED1E1ABBFB340013F194 /* StackedBarChartViewController.xib in Resources */, + 5B4316271AB8D8AE0009FCAA /* Icon-29@2x.png in Resources */, + 5B43162C1AB8D8AE0009FCAA /* Icon-60@3x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5B57BBAB1A9B26AA0036A6CC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5BEAED1B1ABBFB340013F194 /* ScatterChartViewController.m in Sources */, + 5B0CC7851ABB875400665592 /* PieChartViewController.m in Sources */, + 5B57BBBB1A9B26AA0036A6CC /* DemoListViewController.m in Sources */, + 5BD47E651ABB424E008FCEC6 /* BarChartViewController.m in Sources */, + 5BDEDC471ABB871E007D3A60 /* CombinedChartViewController.m in Sources */, + 5BD8F0741AB89CE500566E05 /* LineChart1ViewController.m in Sources */, + 5BEAED401ABC1AC60013F194 /* SinusBarChartViewController.m in Sources */, + 5BEAED251ABC0BE20013F194 /* MultipleBarChartViewController.m in Sources */, + 5B57BBB81A9B26AA0036A6CC /* AppDelegate.m in Sources */, + 5BD47E5B1ABB0263008FCEC6 /* BalloonMarker.swift in Sources */, + 5BEAED2C1ABC160F0013F194 /* CandleStickChartViewController.m in Sources */, + 5BEAED271ABC0BE20013F194 /* MultipleLinesChartViewController.m in Sources */, + 5B8EAF281AB32CF5009697AA /* DemoBaseViewController.m in Sources */, + 5BEAED3B1ABC199F0013F194 /* ColoredLineChartViewController.m in Sources */, + 5BDEDC411ABB7F73007D3A60 /* HorizontalBarChartViewController.m in Sources */, + 5BEAED121ABBFB2B0013F194 /* AnotherBarChartViewController.m in Sources */, + 5BEAED311ABC18F00013F194 /* CubicLineChartViewController.m in Sources */, + 5BEAED1D1ABBFB340013F194 /* StackedBarChartViewController.m in Sources */, + 5BD47E601ABB3C91008FCEC6 /* LineChart2ViewController.m in Sources */, + 5B57BBB51A9B26AA0036A6CC /* main.m in Sources */, + 5BEAED361ABC192F0013F194 /* RadarChartViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 5B57BBD01A9B26AA0036A6CC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 5B57BBD11A9B26AA0036A6CC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5B57BBD31A9B26AA0036A6CC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + INFOPLIST_FILE = "Supporting Files/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = ChartsDemo; + SWIFT_OBJC_BRIDGING_HEADER = "Supporting Files/ChartsDemo-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 5B57BBD41A9B26AA0036A6CC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ENABLE_MODULES = YES; + INFOPLIST_FILE = "Supporting Files/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = ChartsDemo; + SWIFT_OBJC_BRIDGING_HEADER = "Supporting Files/ChartsDemo-Bridging-Header.h"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 5B57BBAA1A9B26AA0036A6CC /* Build configuration list for PBXProject "ChartsDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5B57BBD01A9B26AA0036A6CC /* Debug */, + 5B57BBD11A9B26AA0036A6CC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5B57BBD21A9B26AA0036A6CC /* Build configuration list for PBXNativeTarget "ChartsDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5B57BBD31A9B26AA0036A6CC /* Debug */, + 5B57BBD41A9B26AA0036A6CC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5B57BBA71A9B26AA0036A6CC /* Project object */; +} diff --git a/ChartsDemo/Classes/AppDelegate.h b/ChartsDemo/Classes/AppDelegate.h new file mode 100644 index 0000000000..60359e9915 --- /dev/null +++ b/ChartsDemo/Classes/AppDelegate.h @@ -0,0 +1,22 @@ +// +// AppDelegate.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 23/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + + +@end + diff --git a/ChartsDemo/Classes/AppDelegate.m b/ChartsDemo/Classes/AppDelegate.m new file mode 100644 index 0000000000..60cd377691 --- /dev/null +++ b/ChartsDemo/Classes/AppDelegate.m @@ -0,0 +1,58 @@ +// +// AppDelegate.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 23/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "AppDelegate.h" +#import "DemoListViewController.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + + DemoListViewController *vc = [[DemoListViewController alloc] init]; + UINavigationController *nvc = [[UINavigationController alloc] initWithRootViewController:vc]; + + _window.rootViewController = nvc; + [_window makeKeyAndVisible]; + + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // 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. + // 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. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // 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. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // 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. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // 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. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/ChartsDemo/Classes/Components/BalloonMarker.swift b/ChartsDemo/Classes/Components/BalloonMarker.swift new file mode 100644 index 0000000000..46aed99a26 --- /dev/null +++ b/ChartsDemo/Classes/Components/BalloonMarker.swift @@ -0,0 +1,109 @@ +// +// BalloonMarker.swift +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 19/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation +import UIKit; +import Charts; + +public class BalloonMarker: ChartMarker +{ + public var color: UIColor!; + public var arrowSize = CGSize(width: 15, height: 11); + public var font: UIFont!; + public var insets = UIEdgeInsets(); + public var minimumSize = CGSize(); + + private var labelns: NSString!; + private var _labelSize: CGSize = CGSize(); + private var _size: CGSize = CGSize(); + private var _paragraphStyle: NSMutableParagraphStyle!; + + public init(color: UIColor, font: UIFont, insets: UIEdgeInsets) + { + super.init(); + + self.color = color; + self.font = font; + self.insets = insets; + + _paragraphStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle; + _paragraphStyle.alignment = .Center; + } + + public override var size: CGSize { return _size; } + + public override func draw(#context: CGContext, point: CGPoint) + { + if (labelns === nil) + { + return; + } + + var rect = CGRect(origin: point, size: _size); + rect.origin.x -= _size.width / 2.0; + rect.origin.y -= _size.height; + + CGContextSaveGState(context); + + CGContextSetFillColorWithColor(context, color.CGColor); + CGContextBeginPath(context); + CGContextMoveToPoint(context, + rect.origin.x, + rect.origin.y); + CGContextAddLineToPoint(context, + rect.origin.x + rect.size.width, + rect.origin.y); + CGContextAddLineToPoint(context, + rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height - arrowSize.height); + CGContextAddLineToPoint(context, + rect.origin.x + (rect.size.width + arrowSize.width) / 2.0, + rect.origin.y + rect.size.height - arrowSize.height); + CGContextAddLineToPoint(context, + rect.origin.x + rect.size.width / 2.0, + rect.origin.y + rect.size.height); + CGContextAddLineToPoint(context, + rect.origin.x + (rect.size.width - arrowSize.width) / 2.0, + rect.origin.y + rect.size.height - arrowSize.height); + CGContextAddLineToPoint(context, + rect.origin.x, + rect.origin.y + rect.size.height - arrowSize.height); + CGContextAddLineToPoint(context, + rect.origin.x, + rect.origin.y); + CGContextFillPath(context); + + rect.origin.y += self.insets.top; + rect.size.height -= self.insets.top + self.insets.bottom; + + UIGraphicsPushContext(context); + + labelns.drawInRect(rect, withAttributes: [NSFontAttributeName: self.font, NSParagraphStyleAttributeName: _paragraphStyle]); + + UIGraphicsPopContext(); + + CGContextRestoreGState(context); + } + + public override func refreshContent(#entry: ChartDataEntry, dataSetIndex: Int) + { + var label = entry.value.description; + labelns = label as NSString; + + _labelSize = labelns.sizeWithAttributes([NSFontAttributeName: self.font]); + _size.width = _labelSize.width + self.insets.left + self.insets.right; + _size.height = _labelSize.height + self.insets.top + self.insets.bottom; + _size.width = max(minimumSize.width, _size.width); + _size.height = max(minimumSize.height, _size.height); + } +} \ No newline at end of file diff --git a/ChartsDemo/Classes/DemoBaseViewController.h b/ChartsDemo/Classes/DemoBaseViewController.h new file mode 100644 index 0000000000..4608320633 --- /dev/null +++ b/ChartsDemo/Classes/DemoBaseViewController.h @@ -0,0 +1,26 @@ +// +// DemoBaseViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 13/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import + +@interface DemoBaseViewController : UIViewController +{ +@protected + NSArray *months; + NSArray *parties; +} + +@property (nonatomic, strong) IBOutlet UIButton *optionsButton; +@property (nonatomic, strong) IBOutlet NSArray *options; + +@end diff --git a/ChartsDemo/Classes/DemoBaseViewController.m b/ChartsDemo/Classes/DemoBaseViewController.m new file mode 100644 index 0000000000..df6bebf70a --- /dev/null +++ b/ChartsDemo/Classes/DemoBaseViewController.m @@ -0,0 +1,183 @@ +// +// DemoBaseViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 13/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "DemoBaseViewController.h" + +@interface DemoBaseViewController () + +@property (nonatomic, strong) UITableView *optionsTableView; + +@end + +@implementation DemoBaseViewController + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (self) + { + [self initialize]; + } + return self; +} + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) + { + [self initialize]; + } + return self; +} + +- (void)initialize +{ + self.edgesForExtendedLayout = UIRectEdgeNone; + + months = @[ + @"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", + @"Oct", @"Nov", @"Dec" + ]; + + parties = @[ + @"Party A", @"Party B", @"Party C", @"Party D", @"Party E", @"Party F", + @"Party G", @"Party H", @"Party I", @"Party J", @"Party K", @"Party L", + @"Party M", @"Party N", @"Party O", @"Party P", @"Party Q", @"Party R", + @"Party S", @"Party T", @"Party U", @"Party V", @"Party W", @"Party X", + @"Party Y", @"Party Z" + ]; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)optionTapped:(NSString *)key +{ + +} + +#pragma mark - Actions + +- (IBAction)optionsButtonTapped:(id)sender +{ + if (_optionsTableView) + { + [_optionsTableView removeFromSuperview]; + self.optionsTableView = nil; + return; + } + + self.optionsTableView = [[UITableView alloc] init]; + _optionsTableView.backgroundColor = [UIColor colorWithWhite:0.f alpha:0.9f]; + _optionsTableView.delegate = self; + _optionsTableView.dataSource = self; + + _optionsTableView.translatesAutoresizingMaskIntoConstraints = NO; + + NSMutableArray *constraints = [[NSMutableArray alloc] init]; + + [constraints addObject:[NSLayoutConstraint constraintWithItem:_optionsTableView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.f constant:40.f]]; + + [constraints addObject:[NSLayoutConstraint constraintWithItem:_optionsTableView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:sender attribute:NSLayoutAttributeTrailing multiplier:1.f constant:0]]; + + [constraints addObject:[NSLayoutConstraint constraintWithItem:_optionsTableView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:sender attribute:NSLayoutAttributeBottom multiplier:1.f constant:5.f]]; + + [self.view addSubview:_optionsTableView]; + + [self.view addConstraints:constraints]; + + [_optionsTableView addConstraints:@[ + [NSLayoutConstraint constraintWithItem:_optionsTableView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.f constant:220.f] + ]]; +} + +#pragma mark - UITableViewDelegate, UITableViewDataSource + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + if (tableView == _optionsTableView) + { + return 1; + } + + return 0; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + if (tableView == _optionsTableView) + { + return self.options.count; + } + + return 0; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (tableView == _optionsTableView) + { + return 40.0; + } + + return 44.0; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (tableView == _optionsTableView) + { + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; + if (!cell) + { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"]; + cell.backgroundView = nil; + cell.backgroundColor = UIColor.clearColor; + cell.textLabel.textColor = UIColor.whiteColor; + } + + cell.textLabel.text = self.options[indexPath.row][@"label"]; + + return cell; + } + + return nil; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (tableView == _optionsTableView) + { + [tableView deselectRowAtIndexPath:indexPath animated:YES]; + + if (_optionsTableView) + { + [_optionsTableView removeFromSuperview]; + self.optionsTableView = nil; + } + + [self optionTapped:self.options[indexPath.row][@"key"]]; + } +} + +@end diff --git a/ChartsDemo/Classes/DemoListViewController.h b/ChartsDemo/Classes/DemoListViewController.h new file mode 100644 index 0000000000..433ee23f37 --- /dev/null +++ b/ChartsDemo/Classes/DemoListViewController.h @@ -0,0 +1,20 @@ +// +// DemoListViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 23/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import + +@interface DemoListViewController : UIViewController + + +@end + diff --git a/ChartsDemo/Classes/DemoListViewController.m b/ChartsDemo/Classes/DemoListViewController.m new file mode 100644 index 0000000000..09e7a64215 --- /dev/null +++ b/ChartsDemo/Classes/DemoListViewController.m @@ -0,0 +1,182 @@ +// +// DemoListViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 23/2/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "DemoListViewController.h" +#import "LineChart1ViewController.h" +#import "LineChart2ViewController.h" +#import "BarChartViewController.h" +#import "HorizontalBarChartViewController.h" +#import "CombinedChartViewController.h" +#import "PieChartViewController.h" +#import "ScatterChartViewController.h" +#import "StackedBarChartViewController.h" +#import "AnotherBarChartViewController.h" +#import "MultipleLinesChartViewController.h" +#import "MultipleBarChartViewController.h" +#import "CandleStickChartViewController.h" +#import "CubicLineChartViewController.h" +#import "RadarChartViewController.h" +#import "ColoredLineChartViewController.h" +#import "SinusBarChartViewController.h" + +@interface DemoListViewController () + +@property (nonatomic, strong) IBOutlet UITableView *tableView; +@property (nonatomic, strong) NSArray *itemDefs; +@end + +@implementation DemoListViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Charts Demonstration"; + + self.itemDefs = @[ + @{ + @"title": @"Line Chart", + @"subtitle": @"A simple demonstration of the linechart.", + @"class": LineChart1ViewController.class + }, + @{ + @"title": @"Line Chart (Dual YAxis)", + @"subtitle": @"Demonstration of the linechart with dual y-axis.", + @"class": LineChart2ViewController.class + }, + @{ + @"title": @"Bar Chart", + @"subtitle": @"A simple demonstration of the bar chart.", + @"class": BarChartViewController.class + }, + @{ + @"title": @"Horizontal Bar Chart", + @"subtitle": @"A simple demonstration of the horizontal bar chart.", + @"class": HorizontalBarChartViewController.class + }, + @{ + @"title": @"Combined Chart", + @"subtitle": @"Demonstrates how to create a combined chart (bar and line in this case).", + @"class": CombinedChartViewController.class + }, + @{ + @"title": @"Pie Chart", + @"subtitle": @"A simple demonstration of the pie chart.", + @"class": PieChartViewController.class + }, + @{ + @"title": @"Scatter Chart", + @"subtitle": @"A simple demonstration of the scatter chart.", + @"class": ScatterChartViewController.class + }, + @{ + @"title": @"Stacked Bar Chart", + @"subtitle": @"A simple demonstration of a bar chart with stacked bars.", + @"class": StackedBarChartViewController.class + }, + @{ + @"title": @"Another Bar Chart", + @"subtitle": @"Implementation of a BarChart that only shows values at the bottom.", + @"class": AnotherBarChartViewController.class + }, + @{ + @"title": @"Multiple Lines Chart", + @"subtitle": @"A line chart with multiple DataSet objects. One color per DataSet.", + @"class": MultipleLinesChartViewController.class + }, + @{ + @"title": @"Multiple Bars Chart", + @"subtitle": @"A bar chart with multiple DataSet objects. One multiple colors per DataSet.", + @"class": MultipleBarChartViewController.class + }, + @{ + @"title": @"Candle Stick Chart", + @"subtitle": @"Demonstrates usage of the CandleStickChart.", + @"class": CandleStickChartViewController.class + }, + @{ + @"title": @"Cubic Line Chart", + @"subtitle": @"Demonstrates cubic lines in a LineChart.", + @"class": CubicLineChartViewController.class + }, + @{ + @"title": @"Radar Chart", + @"subtitle": @"Demonstrates the use of a spider-web like (net) chart.", + @"class": RadarChartViewController.class + }, + @{ + @"title": @"Colored Line Chart", + @"subtitle": @"Shows a LineChart with different background and line color.", + @"class": ColoredLineChartViewController.class + }, + @{ + @"title": @"Sinus Bar Chart", + @"subtitle": @"A Bar Chart plotting the sinus function with 8.000 values.", + @"class": SinusBarChartViewController.class + }, + ]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +#pragma mark - UITableViewDataSource, UITableViewDelegate + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +{ + return 1; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return self.itemDefs.count; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath +{ + return 70.f; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + NSDictionary *def = self.itemDefs[indexPath.row]; + + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; + if (!cell) + { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"]; + } + + cell.textLabel.text = def[@"title"]; + cell.detailTextLabel.text = def[@"subtitle"]; + cell.detailTextLabel.numberOfLines = 0; + + return cell; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + NSDictionary *def = self.itemDefs[indexPath.row]; + + Class vcClass = def[@"class"]; + UIViewController *vc = [[vcClass alloc] init]; + + [self.navigationController pushViewController:vc animated:YES]; + + [tableView deselectRowAtIndexPath:indexPath animated:YES]; +} + +@end diff --git a/ChartsDemo/Classes/DemoListViewController.xib b/ChartsDemo/Classes/DemoListViewController.xib new file mode 100644 index 0000000000..699067653c --- /dev/null +++ b/ChartsDemo/Classes/DemoListViewController.xib @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/AnotherBarChartViewController.h b/ChartsDemo/Classes/Demos/AnotherBarChartViewController.h new file mode 100644 index 0000000000..39d3a78912 --- /dev/null +++ b/ChartsDemo/Classes/Demos/AnotherBarChartViewController.h @@ -0,0 +1,20 @@ +// +// AnotherBarChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface AnotherBarChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/AnotherBarChartViewController.m b/ChartsDemo/Classes/Demos/AnotherBarChartViewController.m new file mode 100644 index 0000000000..96f4df0ae1 --- /dev/null +++ b/ChartsDemo/Classes/Demos/AnotherBarChartViewController.m @@ -0,0 +1,201 @@ +// +// AnotherBarChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "AnotherBarChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface AnotherBarChartViewController () + +@property (nonatomic, strong) IBOutlet BarChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation AnotherBarChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Another Bar Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.maxVisibleValueCount = 60; + _chartView.pinchZoomEnabled = NO; + _chartView.drawBarShadowEnabled = NO; + _chartView.drawGridBackgroundEnabled = NO; + + ChartXAxis *xAxis = _chartView.xAxis; + xAxis.labelPosition = XAxisLabelPositionBottom; + xAxis.spaceBetweenLabels = 0.f; + xAxis.drawGridLinesEnabled = NO; + + _chartView.leftAxis.drawGridLinesEnabled = NO; + _chartView.rightAxis.drawGridLinesEnabled = NO; + + _chartView.legend.enabled = NO; + + _sliderX.value = 9.f; + _sliderY.value = 100.f; + [self slidersValueChanged:nil]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + NSMutableArray *yVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float mult = (range + 1); + float val = (float) (arc4random_uniform(mult)) + mult / 3.f; + [yVals addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + } + + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + for (int i = 0; i < count; i++) + { + [xVals addObject:[@((int)((BarChartDataEntry *)yVals[i]).value) stringValue]]; + } + + BarChartDataSet *set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"DataSet"]; + set1.colors = ChartColorTemplates.vordiplom; + set1.drawValuesEnabled = NO; + + NSMutableArray *dataSets = [[NSMutableArray alloc] init]; + [dataSets addObject:set1]; + + BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets]; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlightArrow"]) + { + _chartView.drawHighlightArrowEnabled = !_chartView.isDrawHighlightArrowEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/AnotherBarChartViewController.xib b/ChartsDemo/Classes/Demos/AnotherBarChartViewController.xib new file mode 100644 index 0000000000..8aaf4569f4 --- /dev/null +++ b/ChartsDemo/Classes/Demos/AnotherBarChartViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/BarChartViewController.h b/ChartsDemo/Classes/Demos/BarChartViewController.h new file mode 100644 index 0000000000..4c7d14c444 --- /dev/null +++ b/ChartsDemo/Classes/Demos/BarChartViewController.h @@ -0,0 +1,20 @@ +// +// BarChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface BarChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/BarChartViewController.m b/ChartsDemo/Classes/Demos/BarChartViewController.m new file mode 100644 index 0000000000..749c7beb57 --- /dev/null +++ b/ChartsDemo/Classes/Demos/BarChartViewController.m @@ -0,0 +1,221 @@ +// +// BarChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "BarChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface BarChartViewController () + +@property (nonatomic, strong) IBOutlet BarChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation BarChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Bar Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.drawBarShadowEnabled = YES; + _chartView.drawValueAboveBarEnabled = YES; + + _chartView.maxVisibleValueCount = 60; + _chartView.pinchZoomEnabled = NO; + _chartView.drawGridBackgroundEnabled = NO; + + ChartXAxis *xAxis = _chartView.xAxis; + xAxis.labelPosition = XAxisLabelPositionBottom; + xAxis.labelFont = [UIFont systemFontOfSize:10.f]; + xAxis.drawGridLinesEnabled = NO; + xAxis.spaceBetweenLabels = 2.f; + + ChartYAxis *leftAxis = _chartView.leftAxis; + leftAxis.labelFont = [UIFont systemFontOfSize:10.f]; + leftAxis.labelCount = 8; + leftAxis.valueFormatter = [[NSNumberFormatter alloc] init]; + leftAxis.valueFormatter.maximumFractionDigits = 1; + leftAxis.valueFormatter.negativeSuffix = @" $"; + leftAxis.valueFormatter.positiveSuffix = @" $"; + leftAxis.labelPosition = YAxisLabelPositionOutsideChart; + + ChartYAxis *rightAxis = _chartView.rightAxis; + rightAxis.drawGridLinesEnabled = NO; + rightAxis.labelFont = [UIFont systemFontOfSize:10.f]; + rightAxis.labelCount = 8; + rightAxis.valueFormatter = leftAxis.valueFormatter; + + _chartView.legend.position = ChartLegendPositionBelowChartLeft; + _chartView.legend.form = ChartLegendFormSquare; + _chartView.legend.formSize = 9.f; + _chartView.legend.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:11.f]; + _chartView.legend.xEntrySpace = 4.f; + + _sliderX.value = 11.f; + _sliderY.value = 50.f; + [self slidersValueChanged:nil]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:months[i % 12]]; + } + + NSMutableArray *yVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float mult = (range + 1); + float val = (float) (arc4random_uniform(mult)); + [yVals addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + } + + BarChartDataSet *set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"DataSet"]; + set1.barSpace = 0.35f; + + NSMutableArray *dataSets = [[NSMutableArray alloc] init]; + [dataSets addObject:set1]; + + BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets]; + [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]]; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlightArrow"]) + { + _chartView.drawHighlightArrowEnabled = !_chartView.isDrawHighlightArrowEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/BarChartViewController.xib b/ChartsDemo/Classes/Demos/BarChartViewController.xib new file mode 100644 index 0000000000..7dd3ae83fe --- /dev/null +++ b/ChartsDemo/Classes/Demos/BarChartViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/CandleStickChartViewController.h b/ChartsDemo/Classes/Demos/CandleStickChartViewController.h new file mode 100644 index 0000000000..bd170b4061 --- /dev/null +++ b/ChartsDemo/Classes/Demos/CandleStickChartViewController.h @@ -0,0 +1,20 @@ +// +// CandleStickChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface CandleStickChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/CandleStickChartViewController.m b/ChartsDemo/Classes/Demos/CandleStickChartViewController.m new file mode 100644 index 0000000000..fc9cf6b0d6 --- /dev/null +++ b/ChartsDemo/Classes/Demos/CandleStickChartViewController.m @@ -0,0 +1,231 @@ +// +// CandleStickChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "CandleStickChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface CandleStickChartViewController () + +@property (nonatomic, strong) IBOutlet CandleStickChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation CandleStickChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Candle Stick Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.maxVisibleValueCount = 60; + _chartView.pinchZoomEnabled = NO; + _chartView.drawGridBackgroundEnabled = NO; + + ChartXAxis *xAxis = _chartView.xAxis; + xAxis.labelPosition = XAxisLabelPositionBottom; + xAxis.spaceBetweenLabels = 2.f; + xAxis.drawGridLinesEnabled = NO; + + ChartYAxis *leftAxis = _chartView.leftAxis; + leftAxis.labelCount = 7; + leftAxis.drawGridLinesEnabled = NO; + leftAxis.drawAxisLineEnabled = NO; + leftAxis.startAtZeroEnabled = YES; + + ChartYAxis *rightAxis = _chartView.rightAxis; + rightAxis.enabled = NO; + + _chartView.legend.enabled = NO; + + _sliderX.value = 39.f; + _sliderY.value = 100.f; + [self slidersValueChanged:nil]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:[@(i + 1990) stringValue]]; + } + + NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float mult = (range + 1); + float val = (float) (arc4random_uniform(40)) + mult; + float high =(float) (arc4random_uniform(9)) + 8.f; + float low =(float) (arc4random_uniform(9)) + 8.f; + float open =(float) (arc4random_uniform(6)) + 1.f; + float close =(float) (arc4random_uniform(6)) + 1.f; + BOOL even = i % 2 == 0; + [yVals1 addObject:[[CandleChartDataEntry alloc] initWithXIndex:i shadowH:val + high shadowL:val - low open:even ? val + open : val - open close:even ? val - close : val + close]]; + } + + CandleChartDataSet *set1 = [[CandleChartDataSet alloc] initWithYVals:yVals1 label:@"Data Set"]; + set1.axisDependency = AxisDependencyLeft; + [set1 setColor:[UIColor colorWithWhite:80/255.f alpha:1.f]]; + + CandleChartData *data = [[CandleChartData alloc] initWithXVals:xVals dataSet:set1]; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleFilled"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawFilledEnabled = !set.isDrawFilledEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCircles"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCirclesEnabled = !set.isDrawCirclesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCubic"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCubicEnabled = !set.isDrawCubicEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/CandleStickChartViewController.xib b/ChartsDemo/Classes/Demos/CandleStickChartViewController.xib new file mode 100644 index 0000000000..b439e57460 --- /dev/null +++ b/ChartsDemo/Classes/Demos/CandleStickChartViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/ColoredLineChartViewController.h b/ChartsDemo/Classes/Demos/ColoredLineChartViewController.h new file mode 100644 index 0000000000..464e9bff28 --- /dev/null +++ b/ChartsDemo/Classes/Demos/ColoredLineChartViewController.h @@ -0,0 +1,20 @@ +// +// ColoredLineChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface ColoredLineChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/ColoredLineChartViewController.m b/ChartsDemo/Classes/Demos/ColoredLineChartViewController.m new file mode 100644 index 0000000000..32cea42313 --- /dev/null +++ b/ChartsDemo/Classes/Demos/ColoredLineChartViewController.m @@ -0,0 +1,123 @@ +// +// ColoredLineChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "ColoredLineChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface ColoredLineChartViewController () + +@property (nonatomic, strong) IBOutletCollection(LineChartView) NSArray *chartViews; + +@end + +@implementation ColoredLineChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Colored Line Chart"; + + LineChartData *data = [self dataWithCount:36 range:100]; + [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:7.f]]; + + NSArray *colors = @[ + [UIColor colorWithRed:137/255.f green:230/255.f blue:81/255.f alpha:1.f], + [UIColor colorWithRed:240/255.f green:240/255.f blue:30/255.f alpha:1.f], + [UIColor colorWithRed:89/255.f green:199/255.f blue:250/255.f alpha:1.f], + [UIColor colorWithRed:250/255.f green:104/255.f blue:104/255.f alpha:1.f], + ]; + + for (int i = 0; i < _chartViews.count; i++) + { + [self setupChart:_chartViews[i] data:data color:colors[i % colors.count]]; + } +} + +- (void)setupChart:(LineChartView *)chart data:(LineChartData *)data color:(UIColor *)color +{ + chart.delegate = self; + chart.backgroundColor = color; + + chart.descriptionText = @""; + chart.noDataTextDescription = @"You need to provide data for the chart."; + + chart.drawGridBackgroundEnabled = NO; + chart.dragEnabled = YES; + [chart setScaleEnabled:YES]; + chart.pinchZoomEnabled = NO; + [chart setViewPortOffsetsWithLeft:10.f top:0.f right:10.f bottom:0.f]; + + ChartLegend *l = chart.legend; + l.form = ChartLegendFormCircle; + l.formSize = 6.f; + l.textColor = UIColor.whiteColor; + l.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; + + chart.leftAxis.enabled = NO; + chart.rightAxis.enabled = NO; + chart.xAxis.enabled = NO; + + chart.data = data; + + [chart animateXWithDuration:2.5]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (LineChartData *)dataWithCount:(int)count range:(float)range +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:[@(i % 12) stringValue]]; + } + + NSMutableArray *yVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float val = (float) (arc4random_uniform(range)) + 3; + [yVals addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + } + + LineChartDataSet *set1 = [[LineChartDataSet alloc] initWithYVals:yVals label:@"DataSet 1"]; + + set1.lineWidth = 1.75f; + set1.circleRadius = 3.f; + [set1 setColor:UIColor.whiteColor]; + [set1 setCircleColor:UIColor.whiteColor]; + set1.highlightColor = UIColor.whiteColor; + set1.drawValuesEnabled = NO; + + return [[LineChartData alloc] initWithXVals:xVals dataSet:set1]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/ColoredLineChartViewController.xib b/ChartsDemo/Classes/Demos/ColoredLineChartViewController.xib new file mode 100644 index 0000000000..bc2fef68c4 --- /dev/null +++ b/ChartsDemo/Classes/Demos/ColoredLineChartViewController.xib @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/CombinedChartViewController.h b/ChartsDemo/Classes/Demos/CombinedChartViewController.h new file mode 100644 index 0000000000..d5b090b9df --- /dev/null +++ b/ChartsDemo/Classes/Demos/CombinedChartViewController.h @@ -0,0 +1,20 @@ +// +// CombinedChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface CombinedChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/CombinedChartViewController.m b/ChartsDemo/Classes/Demos/CombinedChartViewController.m new file mode 100644 index 0000000000..19b5a3735f --- /dev/null +++ b/ChartsDemo/Classes/Demos/CombinedChartViewController.m @@ -0,0 +1,166 @@ +// +// CombinedChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "CombinedChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface CombinedChartViewController () + +@property (nonatomic, strong) IBOutlet CombinedChartView *chartView; + +@end + +@implementation CombinedChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Combined Chart"; + + self.options = @[ + @{@"key": @"toggleLineValues", @"label": @"Toggle Line Values"}, + @{@"key": @"toggleBarValues", @"label": @"Toggle Bar Values"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.drawGridBackgroundEnabled = NO; + _chartView.drawBarShadowEnabled = NO; + + _chartView.drawOrder = @[@(DrawOrderBar), @(DrawOrderLine)]; + + ChartYAxis *rightAxis = _chartView.rightAxis; + rightAxis.drawGridLinesEnabled = NO; + + ChartYAxis *leftAxis = _chartView.leftAxis; + leftAxis.drawGridLinesEnabled = NO; + + ChartXAxis *xAxis = _chartView.xAxis; + xAxis.labelPosition = XAxisLabelPositionBothSided; + + CombinedChartData *data = [[CombinedChartData alloc] initWithXVals:months]; + data.lineData = [self generateLineData]; + data.barData = [self generateBarData]; + + _chartView.data = data; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleLineValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + if ([set isKindOfClass:LineChartDataSet.class]) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleBarValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + if ([set isKindOfClass:BarChartDataSet.class]) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } +} + +- (LineChartData *)generateLineData +{ + LineChartData *d = [[LineChartData alloc] init]; + + NSMutableArray *entries = [[NSMutableArray alloc] init]; + + for (int index = 0; index < 12; index++) + { + [entries addObject:[[ChartDataEntry alloc] initWithValue:(arc4random_uniform(15) + 10) xIndex:index]]; + } + + LineChartDataSet *set = [[LineChartDataSet alloc] initWithYVals:entries label:@"Line DataSet"]; + [set setColor:[UIColor colorWithRed:240/255.f green:238/255.f blue:70/255.f alpha:1.f]]; + set.lineWidth = 2.5f; + [set setCircleColor:[UIColor colorWithRed:240/255.f green:238/255.f blue:70/255.f alpha:1.f]]; + set.fillColor = [UIColor colorWithRed:240/255.f green:238/255.f blue:70/255.f alpha:1.f]; + set.drawCubicEnabled = YES; + set.drawValuesEnabled = YES; + set.valueFont = [UIFont systemFontOfSize:10.f]; + set.valueTextColor = [UIColor colorWithRed:240/255.f green:238/255.f blue:70/255.f alpha:1.f]; + + set.axisDependency = AxisDependencyLeft; + + [d addDataSet:set]; + + return d; +} + +- (BarChartData *)generateBarData +{ + BarChartData *d = [[BarChartData alloc] init]; + + NSMutableArray *entries = [[NSMutableArray alloc] init]; + + for (int index = 0; index < 12; index++) + { + [entries addObject:[[BarChartDataEntry alloc] initWithValue:(arc4random_uniform(15) + 30) xIndex:index]]; + } + + BarChartDataSet *set = [[BarChartDataSet alloc] initWithYVals:entries label:@"Bar DataSet"]; + [set setColor:[UIColor colorWithRed:60/255.f green:220/255.f blue:78/255.f alpha:1.f]]; + set.valueTextColor = [UIColor colorWithRed:60/255.f green:220/255.f blue:78/255.f alpha:1.f]; + set.valueFont = [UIFont systemFontOfSize:10.f]; + + set.axisDependency = AxisDependencyLeft; + + [d addDataSet:set]; + + return d; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/CombinedChartViewController.xib b/ChartsDemo/Classes/Demos/CombinedChartViewController.xib new file mode 100644 index 0000000000..e9cf01ea97 --- /dev/null +++ b/ChartsDemo/Classes/Demos/CombinedChartViewController.xib @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/CubicLineChartViewController.h b/ChartsDemo/Classes/Demos/CubicLineChartViewController.h new file mode 100644 index 0000000000..d9388f0cbf --- /dev/null +++ b/ChartsDemo/Classes/Demos/CubicLineChartViewController.h @@ -0,0 +1,20 @@ +// +// CubicLineChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface CubicLineChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/CubicLineChartViewController.m b/ChartsDemo/Classes/Demos/CubicLineChartViewController.m new file mode 100644 index 0000000000..3ee88118dc --- /dev/null +++ b/ChartsDemo/Classes/Demos/CubicLineChartViewController.m @@ -0,0 +1,229 @@ +// +// CubicLineChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "CubicLineChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface CubicLineChartViewController () + +@property (nonatomic, strong) IBOutlet LineChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation CubicLineChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Cubic Line Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleFilled", @"label": @"Toggle Filled"}, + @{@"key": @"toggleCircles", @"label": @"Toggle Circles"}, + @{@"key": @"toggleCubic", @"label": @"Toggle Cubic"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.highlightEnabled = YES; + _chartView.dragEnabled = YES; + [_chartView setScaleEnabled:YES]; + _chartView.pinchZoomEnabled = NO; + _chartView.drawGridBackgroundEnabled = NO; + + _chartView.xAxis.enabled = NO; + _chartView.leftAxis.enabled = NO; + _chartView.rightAxis.enabled = NO; + _chartView.legend.enabled = NO; + + _sliderX.value = 44.f; + _sliderY.value = 100.f; + [self slidersValueChanged:nil]; + [_chartView animateXYWithDurationX:2.0 durationY:2.0]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:[@(i + 1990) stringValue]]; + } + + NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float mult = (range + 1); + float val = (float) (arc4random_uniform(mult)) + 20; + [yVals1 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + } + + LineChartDataSet *set1 = [[LineChartDataSet alloc] initWithYVals:yVals1 label:@"DataSet 1"]; + set1.drawCubicEnabled = YES; + set1.cubicIntensity = 0.2f; + set1.drawCirclesEnabled = NO; + set1.lineWidth = 2.f; + set1.circleRadius = 5.f; + set1.highlightColor = [UIColor colorWithRed:244/255.f green:117/255.f blue:117/255.f alpha:1.f]; + [set1 setColor:[UIColor colorWithRed:104/255.f green:241/255.f blue:175/255.f alpha:1.f]]; + set1.fillColor = [UIColor colorWithRed:51/255.f green:181/255.f blue:229/255.f alpha:1.f]; + + LineChartData *data = [[LineChartData alloc] initWithXVals:xVals dataSet:set1]; + [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:9.f]]; + [data setDrawValues:NO]; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleFilled"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawFilledEnabled = !set.isDrawFilledEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCircles"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCirclesEnabled = !set.isDrawCirclesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCubic"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCubicEnabled = !set.isDrawCubicEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/CubicLineChartViewController.xib b/ChartsDemo/Classes/Demos/CubicLineChartViewController.xib new file mode 100644 index 0000000000..ad4c1ed271 --- /dev/null +++ b/ChartsDemo/Classes/Demos/CubicLineChartViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.h b/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.h new file mode 100644 index 0000000000..d6d0ceefc4 --- /dev/null +++ b/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.h @@ -0,0 +1,20 @@ +// +// HorizontalBarChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface HorizontalBarChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.m b/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.m new file mode 100644 index 0000000000..edbc006f58 --- /dev/null +++ b/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.m @@ -0,0 +1,219 @@ +// +// HorizontalBarChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "HorizontalBarChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface HorizontalBarChartViewController () + +@property (nonatomic, strong) IBOutlet HorizontalBarChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation HorizontalBarChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Horizontal Bar Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.drawBarShadowEnabled = NO; + _chartView.drawValueAboveBarEnabled = YES; + + _chartView.maxVisibleValueCount = 60; + _chartView.pinchZoomEnabled = NO; + _chartView.drawGridBackgroundEnabled = NO; + + ChartXAxis *xAxis = _chartView.xAxis; + xAxis.labelPosition = XAxisLabelPositionBottom; + xAxis.labelFont = [UIFont systemFontOfSize:10.f]; + xAxis.drawAxisLineEnabled = YES; + xAxis.drawGridLinesEnabled = YES; + xAxis.gridLineWidth = .3f; + + ChartYAxis *leftAxis = _chartView.leftAxis; + leftAxis.labelFont = [UIFont systemFontOfSize:10.f]; + leftAxis.drawAxisLineEnabled = YES; + leftAxis.drawGridLinesEnabled = YES; + leftAxis.gridLineWidth = .3f; + + ChartYAxis *rightAxis = _chartView.rightAxis; + rightAxis.labelFont = [UIFont systemFontOfSize:10.f]; + rightAxis.drawAxisLineEnabled = YES; + rightAxis.drawGridLinesEnabled = NO; + + _chartView.legend.position = ChartLegendPositionBelowChartLeft; + _chartView.legend.form = ChartLegendFormSquare; + _chartView.legend.formSize = 8.f; + _chartView.legend.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:11.f]; + _chartView.legend.xEntrySpace = 4.f; + + _sliderX.value = 11.f; + _sliderY.value = 50.f; + [self slidersValueChanged:nil]; + [_chartView animateYWithDuration:2.5]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:months[i % 12]]; + } + + NSMutableArray *yVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float mult = (range + 1); + float val = (float) (arc4random_uniform(mult)); + [yVals addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + } + + BarChartDataSet *set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"DataSet"]; + set1.barSpace = 0.35f; + + NSMutableArray *dataSets = [[NSMutableArray alloc] init]; + [dataSets addObject:set1]; + + BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets]; + [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]]; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlightArrow"]) + { + _chartView.drawHighlightArrowEnabled = !_chartView.isDrawHighlightArrowEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.xib b/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.xib new file mode 100644 index 0000000000..9ce771c86f --- /dev/null +++ b/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/LineChart1ViewController.h b/ChartsDemo/Classes/Demos/LineChart1ViewController.h new file mode 100644 index 0000000000..8b0bab5361 --- /dev/null +++ b/ChartsDemo/Classes/Demos/LineChart1ViewController.h @@ -0,0 +1,20 @@ +// +// LineChart1ViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface LineChart1ViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/LineChart1ViewController.m b/ChartsDemo/Classes/Demos/LineChart1ViewController.m new file mode 100644 index 0000000000..4279433b32 --- /dev/null +++ b/ChartsDemo/Classes/Demos/LineChart1ViewController.m @@ -0,0 +1,255 @@ +// +// LineChart1ViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "LineChart1ViewController.h" +#import "ChartsDemo-Swift.h" + +@interface LineChart1ViewController () + +@property (nonatomic, strong) IBOutlet LineChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation LineChart1ViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Line Chart 1 Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleFilled", @"label": @"Toggle Filled"}, + @{@"key": @"toggleCircles", @"label": @"Toggle Circles"}, + @{@"key": @"toggleCubic", @"label": @"Toggle Cubic"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.highlightEnabled = YES; + _chartView.dragEnabled = YES; + [_chartView setScaleEnabled:YES]; + _chartView.pinchZoomEnabled = YES; + _chartView.highlightIndicatorEnabled = NO; + + BalloonMarker *marker = [[BalloonMarker alloc] initWithColor:[UIColor colorWithWhite:180/255. alpha:1.0] font:[UIFont systemFontOfSize:12.0] insets: UIEdgeInsetsMake(8.0, 8.0, 20.0, 8.0)]; + marker.minimumSize = CGSizeMake(80.f, 40.f); + _chartView.marker = marker; + + _chartView.legend.form = ChartLegendFormLine; + + _sliderX.value = 44.f; + _sliderY.value = 100.f; + [self slidersValueChanged:nil]; + [_chartView animateXWithDuration:2.5]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:[@(i) stringValue]]; + } + + NSMutableArray *yVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float mult = (range + 1); + float val = (float) (arc4random_uniform(mult)) + 3; + [yVals addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + } + + LineChartDataSet *set1 = [[LineChartDataSet alloc] initWithYVals:yVals label:@"DataSet 1"]; + + set1.lineDashLengths = @[@5.f, @2.5f]; + [set1 setColor:UIColor.blackColor]; + [set1 setCircleColor:UIColor.blackColor]; + set1.lineWidth = 1.f; + set1.circleRadius = 3.f; + set1.drawCircleHoleEnabled = NO; + set1.valueFont = [UIFont systemFontOfSize:9.f]; + set1.fillAlpha = 65/255.f; + set1.fillColor = UIColor.blackColor; + + NSMutableArray *dataSets = [[NSMutableArray alloc] init]; + [dataSets addObject:set1]; + + LineChartData *data = [[LineChartData alloc] initWithXVals:xVals dataSets:dataSets]; + + ChartLimitLine *ll1 = [[ChartLimitLine alloc] initWithLimit:130.f label:@"Upper Limit"]; + ll1.lineWidth = 4.f; + ll1.lineDashLengths = @[@5.f, @5.f]; + ll1.labelPosition = LabelPositionRight; + ll1.valueFont = [UIFont systemFontOfSize:10.f]; + + ChartLimitLine *ll2 = [[ChartLimitLine alloc] initWithLimit:-30.f label:@"Lower Limit"]; + ll2.lineWidth = 4.f; + ll2.lineDashLengths = @[@5.f, @5.f]; + ll2.labelPosition = LabelPositionRight; + ll2.valueFont = [UIFont systemFontOfSize:10.f]; + + ChartYAxis *leftAxis = _chartView.leftAxis; + [leftAxis removeAllLimitLines]; + [leftAxis addLimitLine:ll1]; + [leftAxis addLimitLine:ll2]; + leftAxis.customAxisMax = 220.f; + leftAxis.customAxisMin = -50.f; + leftAxis.startAtZeroEnabled = NO; + + _chartView.rightAxis.enabled = NO; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleFilled"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawFilledEnabled = !set.isDrawFilledEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCircles"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCirclesEnabled = !set.isDrawCirclesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCubic"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCubicEnabled = !set.isDrawCubicEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/LineChart1ViewController.xib b/ChartsDemo/Classes/Demos/LineChart1ViewController.xib new file mode 100644 index 0000000000..dc8bca5399 --- /dev/null +++ b/ChartsDemo/Classes/Demos/LineChart1ViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/LineChart2ViewController.h b/ChartsDemo/Classes/Demos/LineChart2ViewController.h new file mode 100644 index 0000000000..07faed62a5 --- /dev/null +++ b/ChartsDemo/Classes/Demos/LineChart2ViewController.h @@ -0,0 +1,20 @@ +// +// LineChart2ViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface LineChart2ViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/LineChart2ViewController.m b/ChartsDemo/Classes/Demos/LineChart2ViewController.m new file mode 100644 index 0000000000..7d68cf8e80 --- /dev/null +++ b/ChartsDemo/Classes/Demos/LineChart2ViewController.m @@ -0,0 +1,275 @@ +// +// LineChart2ViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "LineChart2ViewController.h" +#import "ChartsDemo-Swift.h" + +@interface LineChart2ViewController () + +@property (nonatomic, strong) IBOutlet LineChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation LineChart2ViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Line Chart 2 Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleFilled", @"label": @"Toggle Filled"}, + @{@"key": @"toggleCircles", @"label": @"Toggle Circles"}, + @{@"key": @"toggleCubic", @"label": @"Toggle Cubic"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.highlightEnabled = YES; + _chartView.dragEnabled = YES; + [_chartView setScaleEnabled:YES]; + _chartView.drawGridBackgroundEnabled = NO; + _chartView.pinchZoomEnabled = YES; + + _chartView.backgroundColor = [UIColor colorWithWhite:204/255.f alpha:1.f]; + + _chartView.legend.form = ChartLegendFormLine; + _chartView.legend.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:11.f]; + _chartView.legend.textColor = UIColor.whiteColor; + _chartView.legend.position = ChartLegendPositionBelowChartLeft; + + ChartXAxis *xAxis = _chartView.xAxis; + xAxis.labelFont = [UIFont systemFontOfSize:12.f]; + xAxis.labelTextColor = UIColor.whiteColor; + xAxis.drawGridLinesEnabled = NO; + xAxis.drawAxisLineEnabled = NO; + xAxis.spaceBetweenLabels = 1.0; + + ChartYAxis *leftAxis = _chartView.leftAxis; + leftAxis.labelTextColor = [UIColor colorWithRed:51/255.f green:181/255.f blue:229/255.f alpha:1.f]; + leftAxis.customAxisMax = 200.f; + leftAxis.drawGridLinesEnabled = YES; + + ChartYAxis *rightAxis = _chartView.rightAxis; + rightAxis.labelTextColor = UIColor.redColor; + rightAxis.customAxisMax = 900.0; + rightAxis.startAtZeroEnabled = NO; + rightAxis.customAxisMin = -200.f; + rightAxis.drawGridLinesEnabled = NO; + + _sliderX.value = 19.f; + _sliderY.value = 30.f; + [self slidersValueChanged:nil]; + [_chartView animateXWithDuration:2.5]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:[@(i) stringValue]]; + } + + NSMutableArray *yVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float mult = range / 2.f; + float val = (float) (arc4random_uniform(mult)) + 50; + [yVals addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + } + + LineChartDataSet *set1 = [[LineChartDataSet alloc] initWithYVals:yVals label:@"DataSet 1"]; + set1.axisDependency = AxisDependencyLeft; + [set1 setColor:[UIColor colorWithRed:51/255.f green:181/255.f blue:229/255.f alpha:1.f]]; + [set1 setCircleColor:UIColor.whiteColor]; + set1.lineWidth = 2.f; + set1.circleRadius = 3.f; + set1.fillAlpha = 65/255.f; + set1.fillColor = [UIColor colorWithRed:51/255.f green:181/255.f blue:229/255.f alpha:1.f]; + set1.highlightColor = [UIColor colorWithRed:244/255.f green:117/255.f blue:117/255.f alpha:1.f]; + set1.drawCircleHoleEnabled = NO; + + NSMutableArray *yVals2 = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float mult = range; + float val = (float) (arc4random_uniform(mult)) + 450; + [yVals2 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + } + + LineChartDataSet *set2 = [[LineChartDataSet alloc] initWithYVals:yVals2 label:@"DataSet 2"]; + set2.axisDependency = AxisDependencyRight; + [set2 setColor:UIColor.redColor]; + [set2 setCircleColor:UIColor.whiteColor]; + set2.lineWidth = 2.f; + set2.circleRadius = 3.f; + set2.fillAlpha = 65/255.f; + set2.fillColor = UIColor.redColor; + set2.highlightColor = [UIColor colorWithRed:244/255.f green:117/255.f blue:117/255.f alpha:1.f]; + set2.drawCircleHoleEnabled = NO; + + NSMutableArray *dataSets = [[NSMutableArray alloc] init]; + [dataSets addObject:set2]; + [dataSets addObject:set1]; + + LineChartData *data = [[LineChartData alloc] initWithXVals:xVals dataSets:dataSets]; + [data setValueTextColor:UIColor.whiteColor]; + [data setValueFont:[UIFont systemFontOfSize:9.f]]; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleFilled"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawFilledEnabled = !set.isDrawFilledEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCircles"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCirclesEnabled = !set.isDrawCirclesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCubic"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCubicEnabled = !set.isDrawCubicEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/LineChart2ViewController.xib b/ChartsDemo/Classes/Demos/LineChart2ViewController.xib new file mode 100644 index 0000000000..eb70c43187 --- /dev/null +++ b/ChartsDemo/Classes/Demos/LineChart2ViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/MultipleBarChartViewController.h b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.h new file mode 100644 index 0000000000..81ea95c62c --- /dev/null +++ b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.h @@ -0,0 +1,20 @@ +// +// MultipleBarChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface MultipleBarChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/MultipleBarChartViewController.m b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.m new file mode 100644 index 0000000000..b1b21db6a1 --- /dev/null +++ b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.m @@ -0,0 +1,229 @@ +// +// MultipleBarChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "MultipleBarChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface MultipleBarChartViewController () + +@property (nonatomic, strong) IBOutlet BarChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation MultipleBarChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Multiple Bar Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.pinchZoomEnabled = NO; + _chartView.drawBarShadowEnabled = NO; + _chartView.drawGridBackgroundEnabled = NO; + + BalloonMarker *marker = [[BalloonMarker alloc] initWithColor:[UIColor colorWithWhite:180/255. alpha:1.0] font:[UIFont systemFontOfSize:12.0] insets: UIEdgeInsetsMake(8.0, 8.0, 20.0, 8.0)]; + marker.minimumSize = CGSizeMake(80.f, 40.f); + _chartView.marker = marker; + + ChartLegend *legend = _chartView.legend; + legend.position = ChartLegendPositionRightOfChartInside; + legend.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:11.f]; + + ChartXAxis *xAxis = _chartView.xAxis; + xAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; + + ChartYAxis *leftAxis = _chartView.leftAxis; + leftAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; + leftAxis.valueFormatter = [[NSNumberFormatter alloc] init]; + leftAxis.valueFormatter.maximumFractionDigits = 1; + leftAxis.drawGridLinesEnabled = NO; + leftAxis.spaceTop = 0.25f; + + _chartView.rightAxis.enabled = NO; + _chartView.valueFormatter = [[NSNumberFormatter alloc] init]; + _chartView.valueFormatter.maximumFractionDigits = 1; + + _sliderX.value = 9.f; + _sliderY.value = 100.f; + [self slidersValueChanged:nil]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:[@(i + 1990) stringValue]]; + } + + NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; + NSMutableArray *yVals2 = [[NSMutableArray alloc] init]; + NSMutableArray *yVals3 = [[NSMutableArray alloc] init]; + + float mult = range * 1000.f; + + for (int i = 0; i < count; i++) + { + float val = (float) (arc4random_uniform(mult) + 3.f); + [yVals1 addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + + val = (float) (arc4random_uniform(mult) + 3.f); + [yVals2 addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + + val = (float) (arc4random_uniform(mult) + 3.f); + [yVals3 addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + } + + BarChartDataSet *set1 = [[BarChartDataSet alloc] initWithYVals:yVals1 label:@"Company A"]; + [set1 setColor:[UIColor colorWithRed:104/255.f green:241/255.f blue:175/255.f alpha:1.f]]; + BarChartDataSet *set2 = [[BarChartDataSet alloc] initWithYVals:yVals2 label:@"Company B"]; + [set2 setColor:[UIColor colorWithRed:164/255.f green:228/255.f blue:251/255.f alpha:1.f]]; + BarChartDataSet *set3 = [[BarChartDataSet alloc] initWithYVals:yVals3 label:@"Company C"]; + [set3 setColor:[UIColor colorWithRed:242/255.f green:247/255.f blue:158/255.f alpha:1.f]]; + + NSMutableArray *dataSets = [[NSMutableArray alloc] init]; + [dataSets addObject:set1]; + [dataSets addObject:set2]; + [dataSets addObject:set3]; + + BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets]; + data.groupSpace = 0.8f; + [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]]; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlightArrow"]) + { + _chartView.drawHighlightArrowEnabled = !_chartView.isDrawHighlightArrowEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/MultipleBarChartViewController.xib b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.xib new file mode 100644 index 0000000000..6d27d969b4 --- /dev/null +++ b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.h b/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.h new file mode 100644 index 0000000000..016fec4e76 --- /dev/null +++ b/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.h @@ -0,0 +1,20 @@ +// +// MultipleLinesChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface MultipleLinesChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.m b/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.m new file mode 100644 index 0000000000..875e877421 --- /dev/null +++ b/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.m @@ -0,0 +1,232 @@ +// +// MultipleLinesChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "MultipleLinesChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface MultipleLinesChartViewController () + +@property (nonatomic, strong) IBOutlet LineChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation MultipleLinesChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Multiple Lines Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleFilled", @"label": @"Toggle Filled"}, + @{@"key": @"toggleCircles", @"label": @"Toggle Circles"}, + @{@"key": @"toggleCubic", @"label": @"Toggle Cubic"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.drawGridBackgroundEnabled = NO; + _chartView.highlightEnabled = YES; + _chartView.dragEnabled = YES; + [_chartView setScaleEnabled:YES]; + _chartView.pinchZoomEnabled = NO; + + _chartView.legend.position = ChartLegendPositionRightOfChart; + + _sliderX.value = 19.f; + _sliderY.value = 10.f; + [self slidersValueChanged:nil]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:[@(i) stringValue]]; + } + + NSArray *colors = @[ChartColorTemplates.vordiplom[0], ChartColorTemplates.vordiplom[1], ChartColorTemplates.vordiplom[2]]; + + NSMutableArray *dataSets = [[NSMutableArray alloc] init]; + + for (int z = 0; z < 3; z++) + { + NSMutableArray *values = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float val = (float) (arc4random_uniform(range) + 3); + [values addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + } + + LineChartDataSet *d = [[LineChartDataSet alloc] initWithYVals:values label:[NSString stringWithFormat:@"DataSet %d", z + 1]]; + d.lineWidth = 2.5f; + d.circleRadius = 4.f; + + UIColor *color = colors[z % colors.count]; + [d setColor:color]; + [d setCircleColor:color]; + [dataSets addObject:d]; + } + + ((LineChartDataSet *)dataSets[0]).lineDashLengths = @[@5.f, @5.f]; + ((LineChartDataSet *)dataSets[0]).colors = ChartColorTemplates.vordiplom; + ((LineChartDataSet *)dataSets[0]).circleColors = ChartColorTemplates.vordiplom; + + LineChartData *data = [[LineChartData alloc] initWithXVals:xVals dataSets:dataSets]; + [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:7.f]]; + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleFilled"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawFilledEnabled = !set.isDrawFilledEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCircles"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCirclesEnabled = !set.isDrawCirclesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCubic"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCubicEnabled = !set.isDrawCubicEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.xib b/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.xib new file mode 100644 index 0000000000..00d50dfc9c --- /dev/null +++ b/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/PieChartViewController.h b/ChartsDemo/Classes/Demos/PieChartViewController.h new file mode 100644 index 0000000000..3ef54553ba --- /dev/null +++ b/ChartsDemo/Classes/Demos/PieChartViewController.h @@ -0,0 +1,20 @@ +// +// PieChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface PieChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/PieChartViewController.m b/ChartsDemo/Classes/Demos/PieChartViewController.m new file mode 100644 index 0000000000..3182a57478 --- /dev/null +++ b/ChartsDemo/Classes/Demos/PieChartViewController.m @@ -0,0 +1,216 @@ +// +// PieChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "PieChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface PieChartViewController () + +@property (nonatomic, strong) IBOutlet PieChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation PieChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Pie Bar Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Y-Values"}, + @{@"key": @"toggleXValues", @"label": @"Toggle X-Values"}, + @{@"key": @"togglePercent", @"label": @"Toggle Percent"}, + @{@"key": @"toggleHole", @"label": @"Toggle Hole"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"spin", @"label": @"Spin"}, + @{@"key": @"drawCenter", @"label": @"Draw CenterText"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"} + ]; + + _chartView.delegate = self; + + _chartView.usePercentValuesEnabled = YES; + _chartView.holeTransparent = YES; + _chartView.centerTextFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:12.f]; + _chartView.holeRadiusPercent = 0.45f; + _chartView.transparentCircleRadiusPercent = 0.5f; + _chartView.descriptionText = @""; + _chartView.drawCenterTextEnabled = YES; + _chartView.drawHoleEnabled = YES; + _chartView.rotationAngle = 0.f; + _chartView.rotationEnabled = YES; + _chartView.centerText = @"iOS Charts"; + + ChartLegend *l = _chartView.legend; + l.position = ChartLegendPositionRightOfChart; + l.xEntrySpace = 7.f; + l.yEntrySpace = 5.f; + + _sliderX.value = 3.f; + _sliderY.value = 100.f; + [self slidersValueChanged:nil]; + [_chartView animateXYWithDurationX:1.5 durationY:1.5]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + float mult = range; + + NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; + + // IMPORTANT: In a PieChart, no values (Entry) should have the same xIndex (even if from different DataSets), since no values can be drawn above each other. + for (int i = 0; i < count; i++) + { + [yVals1 addObject:[[BarChartDataEntry alloc] initWithValue:(arc4random_uniform(mult) + mult / 5) xIndex:i]]; + } + + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:parties[i % parties.count]]; + } + + PieChartDataSet *dataSet = [[PieChartDataSet alloc] initWithYVals:yVals1 label:@"Election Results"]; + dataSet.sliceSpace = 3.f; + + // add a lot of colors + + NSMutableArray *colors = [[NSMutableArray alloc] init]; + [colors addObjectsFromArray:ChartColorTemplates.vordiplom]; + [colors addObjectsFromArray:ChartColorTemplates.joyful]; + [colors addObjectsFromArray:ChartColorTemplates.colorful]; + [colors addObjectsFromArray:ChartColorTemplates.liberty]; + [colors addObjectsFromArray:ChartColorTemplates.pastel]; + [colors addObject:[UIColor colorWithRed:51/255.f green:181/255.f blue:229/255.f alpha:1.f]]; + + dataSet.colors = colors; + + PieChartData *data = [[PieChartData alloc] initWithXVals:xVals dataSet:dataSet]; + + NSNumberFormatter *pFormatter = [[NSNumberFormatter alloc] init]; + pFormatter.numberStyle = NSNumberFormatterPercentStyle; + pFormatter.maximumFractionDigits = 1; + pFormatter.multiplier = @1.f; + pFormatter.percentSymbol = @" %"; + [data setValueFormatter:pFormatter]; + [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:11.f]]; + [data setValueTextColor:UIColor.whiteColor]; + + _chartView.data = data; + [_chartView highlightValues:nil]; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleXValues"]) + { + _chartView.drawSliceTextEnabled = !_chartView.isDrawSliceTextEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"togglePercent"]) + { + _chartView.usePercentValuesEnabled = !_chartView.isUsePercentValuesEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHole"]) + { + _chartView.drawHoleEnabled = !_chartView.isDrawHoleEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"drawCenter"]) + { + _chartView.drawCenterTextEnabled = !_chartView.isDrawCenterTextEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"spin"]) + { + [_chartView spin:2.0 fromAngle:_chartView.rotationAngle toAngle:_chartView.rotationAngle + 360.f]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/PieChartViewController.xib b/ChartsDemo/Classes/Demos/PieChartViewController.xib new file mode 100644 index 0000000000..dc173618f5 --- /dev/null +++ b/ChartsDemo/Classes/Demos/PieChartViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/RadarChartViewController.h b/ChartsDemo/Classes/Demos/RadarChartViewController.h new file mode 100644 index 0000000000..e9262c22f6 --- /dev/null +++ b/ChartsDemo/Classes/Demos/RadarChartViewController.h @@ -0,0 +1,20 @@ +// +// RadarChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface RadarChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/RadarChartViewController.m b/ChartsDemo/Classes/Demos/RadarChartViewController.m new file mode 100644 index 0000000000..3d04e4dc6a --- /dev/null +++ b/ChartsDemo/Classes/Demos/RadarChartViewController.m @@ -0,0 +1,183 @@ +// +// RadarChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "RadarChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface RadarChartViewController () + +@property (nonatomic, strong) IBOutlet RadarChartView *chartView; + +@end + +@implementation RadarChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Radar Bar Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleXLabels", @"label": @"Toggle X-Values"}, + @{@"key": @"toggleYLabels", @"label": @"Toggle Y-Values"}, + @{@"key": @"toggleRotate", @"label": @"Toggle Rotate"}, + @{@"key": @"toggleFill", @"label": @"Toggle Fill"}, + @{@"key": @"spin", @"label": @"Spin"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"} + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.webLineWidth = .75f; + _chartView.innerWebLineWidth = 0.375f; + _chartView.webAlpha = 1.f; + + BalloonMarker *marker = [[BalloonMarker alloc] initWithColor:[UIColor colorWithWhite:180/255. alpha:1.0] font:[UIFont systemFontOfSize:12.0] insets: UIEdgeInsetsMake(8.0, 8.0, 20.0, 8.0)]; + marker.minimumSize = CGSizeMake(80.f, 40.f); + _chartView.marker = marker; + + ChartXAxis *xAxis = _chartView.xAxis; + xAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:9.f]; + + ChartYAxis *yAxis = _chartView.yAxis; + yAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:9.f]; + yAxis.labelCount = 5; + yAxis.startAtZeroEnabled = YES; + + ChartLegend *l = _chartView.legend; + l.position = ChartLegendPositionRightOfChart; + l.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; + l.xEntrySpace = 7.f; + l.yEntrySpace = 5.f; + + [self setData]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setData +{ + float mult = 150.f; + int count = 9; + + NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; + NSMutableArray *yVals2 = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [yVals1 addObject:[[ChartDataEntry alloc] initWithValue:(arc4random_uniform(mult) + mult / 2) xIndex:i]]; + [yVals2 addObject:[[ChartDataEntry alloc] initWithValue:(arc4random_uniform(mult) + mult / 2) xIndex:i]]; + } + + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:parties[i % parties.count]]; + } + + RadarChartDataSet *set1 = [[RadarChartDataSet alloc] initWithYVals:yVals1 label:@"Set 1"]; + [set1 setColor:ChartColorTemplates.vordiplom[0]]; + set1.drawFilledEnabled = YES; + set1.lineWidth = 2.f; + + RadarChartDataSet *set2 = [[RadarChartDataSet alloc] initWithYVals:yVals2 label:@"Set 2"]; + [set2 setColor:ChartColorTemplates.vordiplom[4]]; + set2.drawFilledEnabled = YES; + set2.lineWidth = 2.f; + + RadarChartData *data = [[RadarChartData alloc] initWithXVals:xVals dataSets:@[set1, set2]]; + [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:8.f]]; + [data setDrawValues:NO]; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleXLabels"]) + { + _chartView.xAxis.drawLabelsEnabled = !_chartView.xAxis.isDrawLabelsEnabled; + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleYLabels"]) + { + _chartView.yAxis.drawLabelsEnabled = !_chartView.yAxis.isDrawLabelsEnabled; + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleRotate"]) + { + _chartView.rotationEnabled = !_chartView.isRotationEnabled; + } + + if ([key isEqualToString:@"toggleFill"]) + { + for (RadarChartDataSet *set in _chartView.data.dataSets) + { + set.drawFilledEnabled = !set.isDrawFilledEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"spin"]) + { + [_chartView spin:2.0 fromAngle:_chartView.rotationAngle toAngle:_chartView.rotationAngle + 360.f]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/RadarChartViewController.xib b/ChartsDemo/Classes/Demos/RadarChartViewController.xib new file mode 100644 index 0000000000..8bd99e2037 --- /dev/null +++ b/ChartsDemo/Classes/Demos/RadarChartViewController.xib @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/ScatterChartViewController.h b/ChartsDemo/Classes/Demos/ScatterChartViewController.h new file mode 100644 index 0000000000..2b265a76c8 --- /dev/null +++ b/ChartsDemo/Classes/Demos/ScatterChartViewController.h @@ -0,0 +1,20 @@ +// +// ScatterChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface ScatterChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/ScatterChartViewController.m b/ChartsDemo/Classes/Demos/ScatterChartViewController.m new file mode 100644 index 0000000000..f822e85b1f --- /dev/null +++ b/ChartsDemo/Classes/Demos/ScatterChartViewController.m @@ -0,0 +1,249 @@ +// +// ScatterChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "ScatterChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface ScatterChartViewController () + +@property (nonatomic, strong) IBOutlet ScatterChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation ScatterChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Scatter Bar Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.drawGridBackgroundEnabled = NO; + _chartView.highlightEnabled = YES; + _chartView.dragEnabled = YES; + [_chartView setScaleEnabled:YES]; + _chartView.maxVisibleValueCount = 200; + _chartView.pinchZoomEnabled = YES; + + ChartLegend *l = _chartView.legend; + l.position = ChartLegendPositionRightOfChart; + l.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; + + ChartYAxis *yl = _chartView.leftAxis; + yl.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; + + _chartView.rightAxis.enabled = NO; + + ChartXAxis *xl = _chartView.xAxis; + xl.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; + xl.drawGridLinesEnabled = NO; + + _sliderX.value = 45.f; + _sliderY.value = 100.f; + [self slidersValueChanged:nil]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:[@(i) stringValue]]; + } + + NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; + NSMutableArray *yVals2 = [[NSMutableArray alloc] init]; + NSMutableArray *yVals3 = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float val = (float) (arc4random_uniform(range)) + 3; + [yVals1 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + + val = (float) (arc4random_uniform(range)) + 3; + [yVals2 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + + val = (float) (arc4random_uniform(range)) + 3; + [yVals3 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + } + + ScatterChartDataSet *set1 = [[ScatterChartDataSet alloc] initWithYVals:yVals1 label:@"DS 1"]; + set1.scatterShape = ScatterShapeSquare; + [set1 setColor:ChartColorTemplates.colorful[0]]; + ScatterChartDataSet *set2 = [[ScatterChartDataSet alloc] initWithYVals:yVals2 label:@"DS 2"]; + set2.scatterShape = ScatterShapeCircle; + [set2 setColor:ChartColorTemplates.colorful[1]]; + ScatterChartDataSet *set3 = [[ScatterChartDataSet alloc] initWithYVals:yVals3 label:@"DS 3"]; + set3.scatterShape = ScatterShapeCross; + [set3 setColor:ChartColorTemplates.colorful[2]]; + + set1.scatterShapeSize = 8.f; + set2.scatterShapeSize = 8.f; + set3.scatterShapeSize = 8.f; + + NSMutableArray *dataSets = [[NSMutableArray alloc] init]; + [dataSets addObject:set1]; + [dataSets addObject:set2]; + [dataSets addObject:set3]; + + ScatterChartData *data = [[ScatterChartData alloc] initWithXVals:xVals dataSets:dataSets]; + [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:7.f]]; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleFilled"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawFilledEnabled = !set.isDrawFilledEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCircles"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCirclesEnabled = !set.isDrawCirclesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleCubic"]) + { + for (LineChartDataSet *set in _chartView.data.dataSets) + { + set.drawCubicEnabled = !set.isDrawCubicEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/ScatterChartViewController.xib b/ChartsDemo/Classes/Demos/ScatterChartViewController.xib new file mode 100644 index 0000000000..5b8f138e40 --- /dev/null +++ b/ChartsDemo/Classes/Demos/ScatterChartViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/SinusBarChartViewController.h b/ChartsDemo/Classes/Demos/SinusBarChartViewController.h new file mode 100644 index 0000000000..30edfd29a9 --- /dev/null +++ b/ChartsDemo/Classes/Demos/SinusBarChartViewController.h @@ -0,0 +1,20 @@ +// +// SinusBarChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface SinusBarChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/SinusBarChartViewController.m b/ChartsDemo/Classes/Demos/SinusBarChartViewController.m new file mode 100644 index 0000000000..45b8557e82 --- /dev/null +++ b/ChartsDemo/Classes/Demos/SinusBarChartViewController.m @@ -0,0 +1,210 @@ +// +// SinusBarChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "SinusBarChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface SinusBarChartViewController () + +@property (nonatomic, strong) IBOutlet BarChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; + +@end + +@implementation SinusBarChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Sinus Bar Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.drawBarShadowEnabled = NO; + _chartView.drawValueAboveBarEnabled = YES; + _chartView.maxVisibleValueCount = 60; + _chartView.pinchZoomEnabled = NO; + _chartView.drawGridBackgroundEnabled = NO; + + ChartXAxis *xAxis = _chartView.xAxis; + xAxis.labelPosition = XAxisLabelPositionBottom; + xAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; + xAxis.drawGridLinesEnabled = NO; + xAxis.enabled = NO; + + ChartYAxis *leftAxis = _chartView.leftAxis; + leftAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; + leftAxis.labelCount = 6; + leftAxis.startAtZeroEnabled = NO; + leftAxis.axisMinimum = -2.5f; + leftAxis.axisMaximum = 2.5f; + + ChartYAxis *rightAxis = _chartView.rightAxis; + rightAxis.drawGridLinesEnabled = NO; + rightAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; + rightAxis.labelCount = 6; + rightAxis.startAtZeroEnabled = NO; + rightAxis.axisMinimum = -2.5f; + rightAxis.axisMaximum = 2.5f; + + ChartLegend *l = _chartView.legend; + l.position = ChartLegendPositionBelowChartLeft; + l.form = ChartLegendFormSquare; + l.formSize = 9.f; + l.font = [UIFont systemFontOfSize:11.f]; + l.xEntrySpace = 4.f; + + _sliderX.value = 150.0; + [self slidersValueChanged:nil]; + [_chartView animateXYWithDurationX:2.0 durationY:2.0]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + NSMutableArray *entries = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:[@(i) stringValue]]; + [entries addObject:[[BarChartDataEntry alloc] initWithValue:sinf(M_PI * (i % 128) / 64.f) xIndex:i]]; + } + + BarChartDataSet *set = [[BarChartDataSet alloc] initWithYVals:entries label:@"Sinus Function"]; + set.barSpace = .4f; + [set setColor:[UIColor colorWithRed:240/255.f green:120/255.f blue:124/255.f alpha:1.f]]; + + BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSet:set]; + [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]]; + [data setDrawValues:NO]; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlightArrow"]) + { + _chartView.drawHighlightArrowEnabled = !_chartView.isDrawHighlightArrowEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + + [self setDataCount:(_sliderX.value)]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/SinusBarChartViewController.xib b/ChartsDemo/Classes/Demos/SinusBarChartViewController.xib new file mode 100644 index 0000000000..25bed130f6 --- /dev/null +++ b/ChartsDemo/Classes/Demos/SinusBarChartViewController.xib @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Classes/Demos/StackedBarChartViewController.h b/ChartsDemo/Classes/Demos/StackedBarChartViewController.h new file mode 100644 index 0000000000..40bc6fabab --- /dev/null +++ b/ChartsDemo/Classes/Demos/StackedBarChartViewController.h @@ -0,0 +1,20 @@ +// +// StackedBarChartViewController.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "DemoBaseViewController.h" +#import + +@interface StackedBarChartViewController : DemoBaseViewController + +@end diff --git a/ChartsDemo/Classes/Demos/StackedBarChartViewController.m b/ChartsDemo/Classes/Demos/StackedBarChartViewController.m new file mode 100644 index 0000000000..5ad4e46f73 --- /dev/null +++ b/ChartsDemo/Classes/Demos/StackedBarChartViewController.m @@ -0,0 +1,224 @@ +// +// StackedBarChartViewController.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 17/3/15. +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import "StackedBarChartViewController.h" +#import "ChartsDemo-Swift.h" + +@interface StackedBarChartViewController () + +@property (nonatomic, strong) IBOutlet BarChartView *chartView; +@property (nonatomic, strong) IBOutlet UISlider *sliderX; +@property (nonatomic, strong) IBOutlet UISlider *sliderY; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextX; +@property (nonatomic, strong) IBOutlet UITextField *sliderTextY; + +@end + +@implementation StackedBarChartViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + self.title = @"Stacked Bar Chart"; + + self.options = @[ + @{@"key": @"toggleValues", @"label": @"Toggle Values"}, + @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, + @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, + @{@"key": @"animateX", @"label": @"Animate X"}, + @{@"key": @"animateY", @"label": @"Animate Y"}, + @{@"key": @"animateXY", @"label": @"Animate XY"}, + @{@"key": @"toggleStartZero", @"label": @"Toggle StartZero"}, + @{@"key": @"toggleAdjustXLegend", @"label": @"Toggle AdjustXLegend"}, + @{@"key": @"saveToGallery", @"label": @"Save to Camera Roll"}, + @{@"key": @"togglePinchZoom", @"label": @"Toggle PinchZoom"}, + ]; + + _chartView.delegate = self; + + _chartView.descriptionText = @""; + _chartView.noDataTextDescription = @"You need to provide data for the chart."; + + _chartView.maxVisibleValueCount = 60; + _chartView.drawValuesForWholeStackEnabled = YES; + _chartView.pinchZoomEnabled = NO; + _chartView.drawBarShadowEnabled = NO; + _chartView.drawValueAboveBarEnabled = NO; + + ChartYAxis *leftAxis = _chartView.leftAxis; + leftAxis.valueFormatter = [[NSNumberFormatter alloc] init]; + leftAxis.valueFormatter.maximumFractionDigits = 1; + leftAxis.valueFormatter.negativeSuffix = @" $"; + leftAxis.valueFormatter.positiveSuffix = @" $"; + + ChartYAxis *rightAxis = _chartView.rightAxis; + rightAxis.valueFormatter = leftAxis.valueFormatter; + rightAxis.drawGridLinesEnabled = NO; + + ChartXAxis *xAxis = _chartView.xAxis; + xAxis.labelPosition = XAxisLabelPositionTop; + + ChartLegend *l = _chartView.legend; + l.position = ChartLegendPositionBelowChartRight; + l.form = ChartLegendFormSquare; + l.formSize = 8.f; + l.formToTextSpace = 4.f; + l.xEntrySpace = 6.f; + + _sliderX.value = 11.f; + _sliderY.value = 100.f; + [self slidersValueChanged:nil]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)setDataCount:(int)count range:(float)range +{ + NSMutableArray *xVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + [xVals addObject:months[i % 12]]; + } + + NSMutableArray *yVals = [[NSMutableArray alloc] init]; + + for (int i = 0; i < count; i++) + { + float mult = (range + 1); + float val1 = (float) (arc4random_uniform(mult) + mult / 3); + float val2 = (float) (arc4random_uniform(mult) + mult / 3); + float val3 = (float) (arc4random_uniform(mult) + mult / 3); + + [yVals addObject:[[BarChartDataEntry alloc] initWithValues:@[@(val1), @(val2), @(val3)] xIndex:i]]; + } + + BarChartDataSet *set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"Statistics Vienna 2014"]; + set1.colors = @[ChartColorTemplates.vordiplom[0], ChartColorTemplates.vordiplom[1], ChartColorTemplates.vordiplom[2]]; + set1.stackLabels = @[@"Births", @"Divorces", @"Marriages"]; + + NSMutableArray *dataSets = [[NSMutableArray alloc] init]; + [dataSets addObject:set1]; + + NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; + formatter = [[NSNumberFormatter alloc] init]; + formatter.maximumFractionDigits = 1; + formatter.negativeSuffix = @" $"; + formatter.positiveSuffix = @" $"; + + BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets]; + [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:7.f]]; + [data setValueFormatter:formatter]; + + _chartView.data = data; +} + +- (void)optionTapped:(NSString *)key +{ + if ([key isEqualToString:@"toggleValues"]) + { + for (ChartDataSet *set in _chartView.data.dataSets) + { + set.drawValuesEnabled = !set.isDrawValuesEnabled; + } + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlight"]) + { + _chartView.highlightEnabled = !_chartView.isHighlightEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleHighlightArrow"]) + { + _chartView.drawHighlightArrowEnabled = !_chartView.isDrawHighlightArrowEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"toggleStartZero"]) + { + _chartView.leftAxis.startAtZeroEnabled = !_chartView.leftAxis.isStartAtZeroEnabled; + _chartView.rightAxis.startAtZeroEnabled = !_chartView.rightAxis.isStartAtZeroEnabled; + + [_chartView notifyDataSetChanged]; + } + + if ([key isEqualToString:@"animateX"]) + { + [_chartView animateXWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateY"]) + { + [_chartView animateYWithDuration:3.0]; + } + + if ([key isEqualToString:@"animateXY"]) + { + [_chartView animateXYWithDurationX:3.0 durationY:3.0]; + } + + if ([key isEqualToString:@"toggleAdjustXLegend"]) + { + ChartXAxis *xLabels = _chartView.xAxis; + + xLabels.adjustXLabelsEnabled = !xLabels.isAdjustXLabelsEnabled; + + [_chartView setNeedsDisplay]; + } + + if ([key isEqualToString:@"saveToGallery"]) + { + [_chartView saveToCameraRoll]; + } + + if ([key isEqualToString:@"togglePinchZoom"]) + { + _chartView.pinchZoomEnabled = !_chartView.isPinchZoomEnabled; + + [_chartView setNeedsDisplay]; + } +} + +#pragma mark - Actions + +- (IBAction)slidersValueChanged:(id)sender +{ + _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextY.text = [@((int)_sliderY.value) stringValue]; + + [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; +} + +#pragma mark - ChartViewDelegate + +- (void)chartValueSelected:(__nonnull ChartViewBase *)chartView entry:(__nonnull ChartDataEntry *)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(__nonnull ChartHighlight *)highlight +{ + NSLog(@"chartValueSelected"); +} + +- (void)chartValueNothingSelected:(__nonnull ChartViewBase *)chartView +{ + NSLog(@"chartValueNothingSelected"); +} + +@end diff --git a/ChartsDemo/Classes/Demos/StackedBarChartViewController.xib b/ChartsDemo/Classes/Demos/StackedBarChartViewController.xib new file mode 100644 index 0000000000..a8ce2645f5 --- /dev/null +++ b/ChartsDemo/Classes/Demos/StackedBarChartViewController.xib @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Contents.json b/ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..7997783400 --- /dev/null +++ b/ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@2x.png b/ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..33b2e63ab491d39425e104e7ef5916dd41cf6bcb GIT binary patch literal 5677 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 IIm^g@0c)79&;S4c literal 0 HcmV?d00001 diff --git a/ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@3x.png b/ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-29@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..ef18aba824097d907faf79251338789f90356692 GIT binary patch literal 10210 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 Wi~3w&#sBk@?u(@Sr!sMUzyATj7qnOa literal 0 HcmV?d00001 diff --git a/ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png b/ChartsDemo/Resources/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..775e7277c87f33e5894daa86b314de4fd2e4b07a GIT binary patch literal 9266 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 U7%vlQ|B3)&f9CH~?#)m6H}^Ndr~m)} literal 0 HcmV?d00001 diff --git a/ChartsDemo/Resources/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/ChartsDemo/Resources/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a3bc101148bbb81e66044955d9d9ff5f6f6f6d74 GIT binary patch literal 22355 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 literal 0 HcmV?d00001 diff --git a/ChartsDemo/Resources/Images.xcassets/LaunchImage.launchimage/Default@2x.png b/ChartsDemo/Resources/Images.xcassets/LaunchImage.launchimage/Default@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..16802b260b097c043a1d96e7cf24a3a0b5405506 GIT binary patch literal 14035 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sU_QXX1{7KP=)`Xz=R>BmbAYF_vqC^oep+Tu zDg%Q>Z*S<`{M#WS_MQ1LOT_FKZ0zF`?Oq=m9KBOoYV9#L&&f+)I!Wz3dqZo<$?nFwWr=OcEr*QB-~}_ddm%&Y##nf5tTHFh$Puj_O|=mM^RJ+ZCh7 zq<5^<{o3Jp#e?_E*>Ao6UtAitayhfgJg1y++giDgnGI7`TCd|*xaaosgXVq5J0EQx z$?B6mHSrzQuX{IM*l+Mo*XLPOo!INes_)zj?q5E3x}#d4{>80VEaxL5 zmZil#{nx797tdELa7}k}$P>Fxm%R&)IlQ~R{MZg%%Lk7Yi=vHUjr&BL{!L0|+uX5p z;^#c)wJmd`XU#rcZ2!b=tJcEvSJthXmbzEgrADGp)nq}!VV-wZ5gQndR!nH;*(X~N z7|hJFV+~P&vuc^LtVSW9V?_$}% zC(g;#o?pR!;4t5o8O$Glt=IdzLHpB2C%rw@n_s^Y^08UDf936!U$1bzTfK5?$HZT@ zTif=BdL-wiO}J=d=Qg2T{Io*c;nbR%3kCm~E6aMet0tYbx-OgX}hY$ZF$eB z_ovuT6@AM6bWc%v^z_?hop_1i{^ z^-WRCeypMuj_tY@n{)p;XJ7Je^GLjXg>Q3Yp5c)>Gej>dbXOl0EcB2`-!XOm4)#*z z9nZJ<%>6fmE2BqreX+s}E3?&SsuV9&I@DI(N*8~-;_aNm-kZDh-v>{*{?w#y)ry!6 zUk=tq8v9xXRBrONeWiEdmBNPi3i}tyuQ>Q!cjMzCHb_`sNdc^+B->Ug!Z$#{ zIlm}X!Bo#g&p^qJOF==wrYI%ND#*nRsvXF)RmvzSDX`MlFE20GD>v55FG|-pw6wI; zH!#vSGSUUA&@HaaD@m--%_~-h7y>iLCAB!YD6^m>Ge1uOWNuJ**9#9ZqJ{ZIDce;{r$hM(47(sY92y*a4lwkH>ghrG zKo2pGdT2C;fXQJr$pJIMXdVIY(bmss>u0p} zGurwYZT*b4enwkA!>aW&IzvetTZ1;}VwJ6hI42G>R#V51GN(RFU43-?BsRx*vQ z6$Op2j4pl!w!cSL^8)LH#?kfGz{#i41>~cJ0%Y8oriDTy{~vSiNdYI-9Y9-8JYD@< J);T3K0RVdZWCZ{K literal 0 HcmV?d00001 diff --git a/ChartsDemo/Resources/app-icon/Icon-29@2x.png b/ChartsDemo/Resources/app-icon/Icon-29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..33b2e63ab491d39425e104e7ef5916dd41cf6bcb GIT binary patch literal 5677 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 IIm^g@0c)79&;S4c literal 0 HcmV?d00001 diff --git a/ChartsDemo/Resources/app-icon/Icon-29@3x.png b/ChartsDemo/Resources/app-icon/Icon-29@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..ef18aba824097d907faf79251338789f90356692 GIT binary patch literal 10210 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 Wi~3w&#sBk@?u(@Sr!sMUzyATj7qnOa literal 0 HcmV?d00001 diff --git a/ChartsDemo/Resources/app-icon/Icon-40@2x.png b/ChartsDemo/Resources/app-icon/Icon-40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..775e7277c87f33e5894daa86b314de4fd2e4b07a GIT binary patch literal 9266 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 literal 0 HcmV?d00001 diff --git a/ChartsDemo/Resources/app-icon/iTunesArtwork@2x b/ChartsDemo/Resources/app-icon/iTunesArtwork@2x new file mode 100644 index 0000000000000000000000000000000000000000..c55f117d4232955e29fa494716ca3561a14215a4 GIT binary patch literal 333159 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 z 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 U7%vlQ|B3)&f9CH~?#)m6H}^Ndr~m)} literal 0 HcmV?d00001 diff --git a/ChartsDemo/Resources/launch-image/Default-667h@2x.png b/ChartsDemo/Resources/launch-image/Default-667h@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..a3bc101148bbb81e66044955d9d9ff5f6f6f6d74 GIT binary patch literal 22355 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 literal 0 HcmV?d00001 diff --git a/ChartsDemo/Resources/launch-image/Default@2x.png b/ChartsDemo/Resources/launch-image/Default@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..16802b260b097c043a1d96e7cf24a3a0b5405506 GIT binary patch literal 14035 zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sU_QXX1{7KP=)`Xz=R>BmbAYF_vqC^oep+Tu zDg%Q>Z*S<`{M#WS_MQ1LOT_FKZ0zF`?Oq=m9KBOoYV9#L&&f+)I!Wz3dqZo<$?nFwWr=OcEr*QB-~}_ddm%&Y##nf5tTHFh$Puj_O|=mM^RJ+ZCh7 zq<5^<{o3Jp#e?_E*>Ao6UtAitayhfgJg1y++giDgnGI7`TCd|*xaaosgXVq5J0EQx z$?B6mHSrzQuX{IM*l+Mo*XLPOo!INes_)zj?q5E3x}#d4{>80VEaxL5 zmZil#{nx797tdELa7}k}$P>Fxm%R&)IlQ~R{MZg%%Lk7Yi=vHUjr&BL{!L0|+uX5p z;^#c)wJmd`XU#rcZ2!b=tJcEvSJthXmbzEgrADGp)nq}!VV-wZ5gQndR!nH;*(X~N z7|hJFV+~P&vuc^LtVSW9V?_$}% zC(g;#o?pR!;4t5o8O$Glt=IdzLHpB2C%rw@n_s^Y^08UDf936!U$1bzTfK5?$HZT@ zTif=BdL-wiO}J=d=Qg2T{Io*c;nbR%3kCm~E6aMet0tYbx-OgX}hY$ZF$eB z_ovuT6@AM6bWc%v^z_?hop_1i{^ z^-WRCeypMuj_tY@n{)p;XJ7Je^GLjXg>Q3Yp5c)>Gej>dbXOl0EcB2`-!XOm4)#*z z9nZJ<%>6fmE2BqreX+s}E3?&SsuV9&I@DI(N*8~-;_aNm-kZDh-v>{*{?w#y)ry!6 zUk=tq8v9xXRBrONeWiEdmBNPi3i}tyuQ>Q!cjMzCHb_`sNdc^+B->Ug!Z$#{ zIlm}X!Bo#g&p^qJOF==wrYI%ND#*nRsvXF)RmvzSDX`MlFE20GD>v55FG|-pw6wI; zH!#vSGSUUA&@HaaD@m--%_~-h7y>iLCAB!YD6^m>Ge1uOWNuJ**9#9ZqJ{ZIDce;{r$hM(47(sY92y*a4lwkH>ghrG zKo2pGdT2C;fXQJr$pJIMXdVIY(bmss>u0p} zGurwYZT*b4enwkA!>aW&IzvetTZ1;}VwJ6hI42G>R#V51GN(RFU43-?BsRx*vQ z6$Op2j4pl!w!cSL^8)LH#?kfGz{#i41>~cJ0%Y8oriDTy{~vSiNdYI-9Y9-8JYD@< J);T3K0RVdZWCZ{K literal 0 HcmV?d00001 diff --git a/ChartsDemo/Supporting Files/ChartsDemo-Bridging-Header.h b/ChartsDemo/Supporting Files/ChartsDemo-Bridging-Header.h new file mode 100644 index 0000000000..1b2cb5d6d0 --- /dev/null +++ b/ChartsDemo/Supporting Files/ChartsDemo-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/ChartsDemo/Supporting Files/Info.plist b/ChartsDemo/Supporting Files/Info.plist new file mode 100644 index 0000000000..97caf61173 --- /dev/null +++ b/ChartsDemo/Supporting Files/Info.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.dcg.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ChartsDemo/Supporting Files/main.m b/ChartsDemo/Supporting Files/main.m new file mode 100644 index 0000000000..f4d736286e --- /dev/null +++ b/ChartsDemo/Supporting Files/main.m @@ -0,0 +1,22 @@ +// +// main.m +// Charts +// +// Created by Daniel Cohen Gindi on 23/2/15. + +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +}