├── .gitignore ├── CHANGELOG.md ├── Examples ├── AppDelegate.swift ├── DetailViewController.swift ├── Examples │ ├── AreasExample.swift │ ├── BarsExample.swift │ ├── BarsPlusMinusAndLinesExample.swift │ ├── BarsPlusMinusWithGradientExample.swift │ ├── BubbleExample.swift │ ├── CandleStickExample.swift │ ├── CandleStickInteractiveExample.swift │ ├── Convenience │ │ ├── ConvenienceBarsExample.swift │ │ └── ConvenienceLinesExample.swift │ ├── CoordsExample.swift │ ├── CubicLinesExample.swift │ ├── CubicLinesWithGradientExample.swift │ ├── CustomUnitsExample.swift │ ├── Env.swift │ ├── EqualSpacingExample.swift │ ├── ExamplesDefaults.swift │ ├── GroupedAndStackedBarsExample.swift │ ├── GroupedBarsExample.swift │ ├── HelloWorld.swift │ ├── MultiTrackerExample.swift │ ├── MultipleAxesExample.swift │ ├── MultipleAxesInteractiveExample.swift │ ├── MultipleLabelsExample.swift │ ├── NotNumericExample.swift │ ├── NotificationsExample.swift │ ├── RangedAxisExample.swift │ ├── ScatterExample.swift │ ├── StackedBarsExample.swift │ ├── TargetExample.swift │ ├── TrackerExample.swift │ └── TrendlineExample.swift ├── HorizontalGuide.swift ├── Interface │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ ├── Main.storyboard │ │ └── Main_iPhone.storyboard │ └── Images.xcassets │ │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-40.png │ │ ├── Icon-40@2x.png │ │ ├── Icon-40@3x.png │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-72.png │ │ ├── Icon-72@2x.png │ │ ├── Icon-76.png │ │ ├── Icon-76@2x.png │ │ ├── Icon-83.5@2x.png │ │ ├── Icon-Small-50.png │ │ ├── Icon-Small-50@2x.png │ │ ├── Icon-Small.png │ │ ├── Icon-Small@2x.png │ │ ├── Icon-Small@3x.png │ │ ├── Icon.png │ │ ├── Icon@2x.png │ │ ├── NotificationIcon@2x.png │ │ ├── NotificationIcon@3x.png │ │ ├── NotificationIcon~ipad.png │ │ ├── NotificationIcon~ipad@2x.png │ │ └── ios-marketing.png │ │ └── LaunchImage.launchimage │ │ └── Contents.json ├── MasterViewController.swift ├── PointGuide.swift ├── String.swift ├── Supporting Files │ └── Info.plist ├── UIColor.swift └── VerticalGuide.swift ├── Issue_template.md ├── LICENSE ├── Package.swift ├── README.md ├── Screenshots ├── IMG_0022.jpeg ├── IMG_0023.jpeg ├── IMG_0024.jpeg ├── IMG_0026.jpeg ├── IMG_0027.jpeg ├── IMG_0029.jpeg ├── IMG_0033.jpeg ├── IMG_0034.jpeg ├── IMG_0037.jpeg ├── IMG_0039.jpeg ├── IMG_0040.jpeg ├── IMG_0041.jpeg ├── IMG_0101.jpeg ├── IMG_0102.jpeg ├── IMG_1328.PNG ├── IMG_1330.PNG ├── IMG_1332.PNG ├── IMG_1334.PNG ├── index.html ├── layers.png ├── multi-chart-touch.jpg └── small │ ├── IMG_0022.jpeg │ ├── IMG_0023.jpeg │ ├── IMG_0024.jpeg │ ├── IMG_0025.png │ ├── IMG_0026.jpeg │ ├── IMG_0027.jpeg │ ├── IMG_0028.jpeg │ ├── IMG_0029.jpeg │ ├── IMG_0031.jpeg │ ├── IMG_0033.jpeg │ ├── IMG_0034.jpeg │ ├── IMG_0037.jpeg │ ├── IMG_0038.jpeg │ ├── IMG_0039.jpeg │ ├── IMG_0040.jpeg │ ├── IMG_0041.jpeg │ ├── IMG_0101.jpeg │ └── IMG_0102.jpeg ├── SwiftCharts.podspec ├── SwiftCharts.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── Examples.xcscheme │ ├── SwiftCharts tvOS.xcscheme │ └── SwiftCharts.xcscheme └── SwiftCharts ├── Array_String.swift ├── Axis ├── ChartAxis.swift ├── ChartAxisGeneratorMultiplier.swift ├── ChartAxisLabel.swift ├── ChartAxisLabelGeneratorDate.swift ├── ChartAxisLabelsConflictSolver.swift ├── ChartAxisLabelsConflictSolverMoveUpDown.swift ├── ChartAxisLabelsGenerator.swift ├── ChartAxisLabelsGeneratorBase.swift ├── ChartAxisLabelsGeneratorBasic.swift ├── ChartAxisLabelsGeneratorFixed.swift ├── ChartAxisLabelsGeneratorFunc.swift ├── ChartAxisLabelsGeneratorNumber.swift ├── ChartAxisLabelsGeneratorNumberSuffix.swift ├── ChartAxisLayer.swift ├── ChartAxisLayerDefault.swift ├── ChartAxisModel.swift ├── ChartAxisValueStaticGenerator.swift ├── ChartAxisValuesGenerator.swift ├── ChartAxisValuesGeneratorDate.swift ├── ChartAxisValuesGeneratorFixed.swift ├── ChartAxisValuesGeneratorFixedNonOverlapping.swift ├── ChartAxisValuesGeneratorNice.swift ├── ChartAxisValuesGeneratorXDividers.swift ├── ChartAxisValuesGeneratorXFixedNonOverlapping.swift ├── ChartAxisValuesGeneratorYFixedNonOverlapping.swift ├── ChartAxisX.swift ├── ChartAxisXHighLayerDefault.swift ├── ChartAxisXLayerDefault.swift ├── ChartAxisXLowLayerDefault.swift ├── ChartAxisY.swift ├── ChartAxisYHighLayerDefault.swift ├── ChartAxisYLayerDefault.swift └── ChartAxisYLowLayerDefault.swift ├── AxisValues ├── ChartAxisValue.swift ├── ChartAxisValueDate.swift ├── ChartAxisValueDouble.swift ├── ChartAxisValueDoubleScreenLoc.swift ├── ChartAxisValueFloat.swift ├── ChartAxisValueFloatScreenLoc.swift ├── ChartAxisValueInt.swift └── ChartAxisValueString.swift ├── CGPoint.swift ├── CGRect.swift ├── Chart.swift ├── ChartAxisValueArray.swift ├── ChartCoordsSpace.swift ├── ChartLineModel.swift ├── ChartPoint ├── ChartPoint.swift ├── ChartPointBubble.swift └── ChartPointCandleStick.swift ├── ChartViewsConflictSolver.swift ├── Convenience ├── BarsChart.swift ├── ChartConfig.swift └── LineChart.swift ├── Drawers ├── ChartContextDrawer.swift ├── ChartDrawerFunctions.swift ├── ChartLabelDrawer.swift └── ChartLineDrawer.swift ├── Globals.swift ├── Int.swift ├── Layers ├── ChartBarsLayer.swift ├── ChartCandleStickLayer.swift ├── ChartCoordsSpaceLayer.swift ├── ChartDividersLayer.swift ├── ChartGroupedBarsLayer.swift ├── ChartGuideLinesLayer.swift ├── ChartLayer.swift ├── ChartLayerBase.swift ├── ChartPointsAreaLayer.swift ├── ChartPointsBubbleLayer.swift ├── ChartPointsCandleStickViewsLayer.swift ├── ChartPointsLayer.swift ├── ChartPointsLineLayer.swift ├── ChartPointsLineTrackerLayer.swift ├── ChartPointsScatterLayer.swift ├── ChartPointsSingleViewLayer.swift ├── ChartPointsTouchHighlightLayer.swift ├── ChartPointsTrackerLayer.swift ├── ChartPointsViewsLayer.swift ├── ChartShowCoordsLinesLayer.swift ├── ChartStackedBarsLayer.swift └── GroupedBarsCompanionsLayer.swift ├── NSDate.swift ├── Optional.swift ├── Pannable.swift ├── String.swift ├── Supporting Files ├── Info.plist └── SwiftCharts.h ├── UIColor.swift ├── Utils ├── ChartNiceNumberCalculator.swift ├── ChartTextUtils.swift ├── ChartTimeUtils.swift ├── ChartViewSelector.swift ├── ChartViewSelectorAlpha.swift ├── ChartViewSelectorBrightness.swift ├── Operators.swift └── Trendlines │ └── TrendlineGenerator.swift ├── Views ├── CatmullPathGenerator.swift ├── ChartAreasView.swift ├── ChartCandleStickView.swift ├── ChartLinesView.swift ├── ChartPointEllipseView.swift ├── ChartPointTargetingView.swift ├── ChartPointTextCircleView.swift ├── ChartPointViewBar.swift ├── ChartPointViewBarGreyOut.swift ├── ChartPointViewBarStacked.swift ├── ChartViewAlphaAnimator.swift ├── ChartViewAnimator.swift ├── ChartViewAnimators.swift ├── ChartViewGrowAnimator.swift ├── CubicLinePathGenerator.swift ├── HandlingLabel.swift ├── HandlingView.swift ├── InfoBubble.swift └── StraightLinePathGenerator.swift └── Zoomable.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | .DS_Store 20 | 21 | # CocoaPods 22 | # 23 | # We recommend against adding the Pods directory to your .gitignore. However 24 | # you should judge for yourself, the pros and cons are mentioned at: 25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 26 | # 27 | Pods 28 | 29 | # Carthage 30 | # 31 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 32 | # Carthage/Checkouts 33 | 34 | Carthage/Build 35 | -------------------------------------------------------------------------------- /Examples/Examples/Convenience/ConvenienceBarsExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConvenienceBarsExample.swift 3 | // Examples 4 | // 5 | // Created by ischuetz on 19/07/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftCharts 11 | 12 | class ConvenienceBarsExample: UIViewController { 13 | 14 | fileprivate var chart: Chart? // arc 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | let chartConfig = BarsChartConfig( 20 | chartSettings: ExamplesDefaults.chartSettingsWithPanZoom, 21 | valsAxisConfig: ChartAxisConfig(from: 0, to: 8, by: 2), 22 | xAxisLabelSettings: ExamplesDefaults.labelSettings, 23 | yAxisLabelSettings: ExamplesDefaults.labelSettings.defaultVertical() 24 | ) 25 | 26 | let chart = BarsChart( 27 | frame: ExamplesDefaults.chartFrame(view.bounds), 28 | chartConfig: chartConfig, 29 | xTitle: "X axis", 30 | yTitle: "Y axis", 31 | bars: [ 32 | ("A", 2), 33 | ("B", 4.5), 34 | ("C", 3), 35 | ("D", 5.4), 36 | ("E", 6.8), 37 | ("F", 0.5) 38 | ], 39 | color: UIColor.red, 40 | barWidth: Env.iPad ? 40 : 20 41 | ) 42 | 43 | view.addSubview(chart.view) 44 | self.chart = chart 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Examples/Examples/Convenience/ConvenienceLinesExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConvenienceLinesExample.swift 3 | // Examples 4 | // 5 | // Created by ischuetz on 19/07/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftCharts 11 | 12 | class ConvenienceLinesExample: UIViewController { 13 | 14 | fileprivate var chart: Chart? // arc 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | let chartConfig = ChartConfigXY( 20 | chartSettings: ExamplesDefaults.chartSettingsWithPanZoom, 21 | xAxisConfig: ChartAxisConfig(from: 2, to: 14, by: 2), 22 | yAxisConfig: ChartAxisConfig(from: 0, to: 14, by: 2), 23 | xAxisLabelSettings: ExamplesDefaults.labelSettings, 24 | yAxisLabelSettings: ExamplesDefaults.labelSettings.defaultVertical() 25 | ) 26 | 27 | let chart = LineChart( 28 | frame: ExamplesDefaults.chartFrame(view.bounds), 29 | chartConfig: chartConfig, 30 | xTitle: "X axis", 31 | yTitle: "Y axis", 32 | lines: [ 33 | (chartPoints: [(2.0, 10.6), (4.2, 5.1), (7.3, 3.0), (8.1, 5.5), (14.0, 8.0)], color: UIColor.red), 34 | (chartPoints: [(2.0, 2.6), (4.2, 4.1), (7.3, 1.0), (8.1, 11.5), (14.0, 3.0)], color: UIColor.blue) 35 | ] 36 | ) 37 | 38 | view.addSubview(chart.view) 39 | self.chart = chart 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Examples/Examples/CubicLinesExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CubicLinesExample.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 04/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftCharts 11 | 12 | class CubicLinesExample: UIViewController { 13 | 14 | fileprivate var chart: Chart? // arc 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont) 20 | 21 | let chartPoints = [(0, 0), (4, 4), (8, 11), (9, 2), (11, 10), (12, 3), (15, 18), (18, 10), (20, 15)].map{ChartPoint(x: ChartAxisValueInt($0.0, labelSettings: labelSettings), y: ChartAxisValueInt($0.1))} 22 | 23 | let xValues = chartPoints.map{$0.x} 24 | let yValues = ChartAxisValuesStaticGenerator.generateYAxisValuesWithChartPoints(chartPoints, minSegmentCount: 10, maxSegmentCount: 20, multiple: 2, axisValueGenerator: {ChartAxisValueDouble($0, labelSettings: labelSettings)}, addPaddingSegmentIfEdge: false) 25 | 26 | let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings)) 27 | let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings.defaultVertical())) 28 | let chartFrame = ExamplesDefaults.chartFrame(view.bounds) 29 | 30 | let chartSettings = ExamplesDefaults.chartSettingsWithPanZoom 31 | 32 | let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel) 33 | let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame) 34 | 35 | let lineModel = ChartLineModel(chartPoints: chartPoints, lineColor: UIColor.purple, lineWidth: 2, animDuration: 1, animDelay: 0) 36 | let chartPointsLineLayer = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel], pathGenerator: CatmullPathGenerator()) // || CubicLinePathGenerator 37 | 38 | let settings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.black, linesWidth: ExamplesDefaults.guidelinesWidth) 39 | let guidelinesLayer = ChartGuideLinesDottedLayer(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, settings: settings) 40 | 41 | let chart = Chart( 42 | frame: chartFrame, 43 | innerFrame: innerFrame, 44 | settings: chartSettings, 45 | layers: [ 46 | xAxisLayer, 47 | yAxisLayer, 48 | guidelinesLayer, 49 | chartPointsLineLayer 50 | ] 51 | ) 52 | 53 | view.addSubview(chart.view) 54 | self.chart = chart 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Examples/Examples/CubicLinesWithGradientExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CubicLinesExample.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 04/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftCharts 11 | 12 | class CubicLinesWithGradientExample: UIViewController { 13 | 14 | fileprivate var chart: Chart? // arc 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont) 20 | 21 | let chartPoints = [(0, 0), (4, 4), (8, 11), (9, 2), (11, 10), (12, 3), (15, 18), (18, 10), (20, 15)].map{ChartPoint(x: ChartAxisValueInt($0.0, labelSettings: labelSettings), y: ChartAxisValueInt($0.1))} 22 | 23 | let xValues = chartPoints.map{$0.x} 24 | let yValues = ChartAxisValuesStaticGenerator.generateYAxisValuesWithChartPoints(chartPoints, minSegmentCount: 10, maxSegmentCount: 20, multiple: 2, axisValueGenerator: {ChartAxisValueDouble($0, labelSettings: labelSettings)}, addPaddingSegmentIfEdge: false) 25 | 26 | let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings)) 27 | let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings.defaultVertical())) 28 | let chartFrame = ExamplesDefaults.chartFrame(view.bounds) 29 | 30 | let chartSettings = ExamplesDefaults.chartSettingsWithPanZoom 31 | 32 | let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel) 33 | let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame) 34 | 35 | let lineModel = ChartLineModel(chartPoints: chartPoints, lineColors: [UIColor.yellow, UIColor.red], lineWidth: 2, animDuration: 1, animDelay: 0) 36 | let chartPointsLineLayer = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel], pathGenerator: CatmullPathGenerator()) // || CubicLinePathGenerator 37 | 38 | let settings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.black, linesWidth: ExamplesDefaults.guidelinesWidth) 39 | let guidelinesLayer = ChartGuideLinesDottedLayer(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, settings: settings) 40 | 41 | let chart = Chart( 42 | frame: chartFrame, 43 | innerFrame: innerFrame, 44 | settings: chartSettings, 45 | layers: [ 46 | xAxisLayer, 47 | yAxisLayer, 48 | guidelinesLayer, 49 | chartPointsLineLayer 50 | ] 51 | ) 52 | 53 | view.addSubview(chart.view) 54 | self.chart = chart 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Examples/Examples/Env.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Env.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 07/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class Env { 12 | 13 | static var iPad: Bool { 14 | return UIDevice.current.userInterfaceIdiom == .pad 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Examples/Examples/EqualSpacingExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EqualSpacingExample.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 04/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftCharts 11 | 12 | class EqualSpacingExample: UIViewController { 13 | 14 | fileprivate var chart: Chart? // arc 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont) 20 | 21 | let chartPoints = [ 22 | ChartPoint(x: ChartAxisValueDoubleScreenLoc(screenLocDouble: 1, actualDouble: 2, labelSettings: labelSettings), y: ChartAxisValueDouble(2)), 23 | ChartPoint(x: ChartAxisValueDoubleScreenLoc(screenLocDouble: 2, actualDouble: 100, labelSettings: labelSettings), y: ChartAxisValueDouble(5)), 24 | ChartPoint(x: ChartAxisValueDoubleScreenLoc(screenLocDouble: 3, actualDouble: 100.1, labelSettings: labelSettings), y: ChartAxisValueDouble(1)), 25 | ChartPoint(x: ChartAxisValueDoubleScreenLoc(screenLocDouble: 4, actualDouble: 900000, labelSettings: labelSettings), y: ChartAxisValueDouble(10)) 26 | ] 27 | 28 | let xValues = chartPoints.map{$0.x} 29 | let yValues = ChartAxisValuesStaticGenerator.generateYAxisValuesWithChartPoints(chartPoints, minSegmentCount: 10, maxSegmentCount: 20, multiple: 2, axisValueGenerator: {ChartAxisValueDouble($0, labelSettings: labelSettings)}, addPaddingSegmentIfEdge: false) 30 | 31 | let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings)) 32 | let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings.defaultVertical())) 33 | let chartFrame = ExamplesDefaults.chartFrame(view.bounds) 34 | var chartSettings = ExamplesDefaults.chartSettingsWithPanZoom 35 | chartSettings.trailing = 40 36 | let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel) 37 | let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame) 38 | 39 | let lineModel = ChartLineModel(chartPoints: chartPoints, lineColor: UIColor.red, animDuration: 1, animDelay: 0) 40 | let chartPointsLineLayer = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel]) 41 | 42 | let settings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.black, linesWidth: ExamplesDefaults.guidelinesWidth) 43 | let guidelinesLayer = ChartGuideLinesDottedLayer(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, settings: settings) 44 | 45 | let chart = Chart( 46 | frame: chartFrame, 47 | innerFrame: innerFrame, 48 | settings: chartSettings, 49 | layers: [ 50 | xAxisLayer, 51 | yAxisLayer, 52 | guidelinesLayer, 53 | chartPointsLineLayer 54 | ] 55 | ) 56 | 57 | view.addSubview(chart.view) 58 | self.chart = chart 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Examples/Examples/ExamplesDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExamplesDefaults.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 04/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftCharts 11 | 12 | struct ExamplesDefaults { 13 | 14 | static var chartSettings: ChartSettings { 15 | if Env.iPad { 16 | return iPadChartSettings 17 | } else { 18 | return iPhoneChartSettings 19 | } 20 | } 21 | 22 | static var chartSettingsWithPanZoom: ChartSettings { 23 | if Env.iPad { 24 | return iPadChartSettingsWithPanZoom 25 | } else { 26 | return iPhoneChartSettingsWithPanZoom 27 | } 28 | } 29 | 30 | fileprivate static var iPadChartSettings: ChartSettings { 31 | var chartSettings = ChartSettings() 32 | chartSettings.leading = 20 33 | chartSettings.top = 20 34 | chartSettings.trailing = 20 35 | chartSettings.bottom = 20 36 | chartSettings.labelsToAxisSpacingX = 10 37 | chartSettings.labelsToAxisSpacingY = 10 38 | chartSettings.axisTitleLabelsToLabelsSpacing = 5 39 | chartSettings.axisStrokeWidth = 1 40 | chartSettings.spacingBetweenAxesX = 15 41 | chartSettings.spacingBetweenAxesY = 15 42 | chartSettings.labelsSpacing = 0 43 | return chartSettings 44 | } 45 | 46 | fileprivate static var iPhoneChartSettings: ChartSettings { 47 | var chartSettings = ChartSettings() 48 | chartSettings.leading = 10 49 | chartSettings.top = 10 50 | chartSettings.trailing = 10 51 | chartSettings.bottom = 10 52 | chartSettings.labelsToAxisSpacingX = 5 53 | chartSettings.labelsToAxisSpacingY = 5 54 | chartSettings.axisTitleLabelsToLabelsSpacing = 4 55 | chartSettings.axisStrokeWidth = 0.2 56 | chartSettings.spacingBetweenAxesX = 8 57 | chartSettings.spacingBetweenAxesY = 8 58 | chartSettings.labelsSpacing = 0 59 | return chartSettings 60 | } 61 | 62 | fileprivate static var iPadChartSettingsWithPanZoom: ChartSettings { 63 | var chartSettings = iPadChartSettings 64 | chartSettings.zoomPan.panEnabled = true 65 | chartSettings.zoomPan.zoomEnabled = true 66 | return chartSettings 67 | } 68 | 69 | fileprivate static var iPhoneChartSettingsWithPanZoom: ChartSettings { 70 | var chartSettings = iPhoneChartSettings 71 | chartSettings.zoomPan.panEnabled = true 72 | chartSettings.zoomPan.zoomEnabled = true 73 | return chartSettings 74 | } 75 | 76 | static func chartFrame(_ containerBounds: CGRect) -> CGRect { 77 | return CGRect(x: 0, y: 70, width: containerBounds.size.width, height: containerBounds.size.height - 70) 78 | } 79 | 80 | static var labelSettings: ChartLabelSettings { 81 | return ChartLabelSettings(font: ExamplesDefaults.labelFont) 82 | } 83 | 84 | static var labelFont: UIFont { 85 | return ExamplesDefaults.fontWithSize(Env.iPad ? 14 : 11) 86 | } 87 | 88 | static var labelFontSmall: UIFont { 89 | return ExamplesDefaults.fontWithSize(Env.iPad ? 12 : 10) 90 | } 91 | 92 | static func fontWithSize(_ size: CGFloat) -> UIFont { 93 | return UIFont(name: "Helvetica", size: size) ?? UIFont.systemFont(ofSize: size) 94 | } 95 | 96 | static var guidelinesWidth: CGFloat { 97 | return Env.iPad ? 0.5 : 0.1 98 | } 99 | 100 | static var minBarSpacing: CGFloat { 101 | return Env.iPad ? 10 : 5 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Examples/Examples/HelloWorld.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HelloWorld.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 05/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftCharts 11 | 12 | class HelloWorld: UIViewController { 13 | 14 | fileprivate var chart: Chart? // arc 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | // map model data to chart points 20 | let chartPoints: [ChartPoint] = [(2, 2), (4, 4), (6, 6), (8, 8), (8, 10), (15, 15)].map{ChartPoint(x: ChartAxisValueInt($0.0), y: ChartAxisValueInt($0.1))} 21 | 22 | let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont) 23 | 24 | let generator = ChartAxisGeneratorMultiplier(2) 25 | let labelsGenerator = ChartAxisLabelsGeneratorFunc {scalar in 26 | return ChartAxisLabel(text: "\(scalar)", settings: labelSettings) 27 | } 28 | 29 | let xGenerator = ChartAxisGeneratorMultiplier(2) 30 | 31 | let xModel = ChartAxisModel(firstModelValue: 0, lastModelValue: 16, axisTitleLabels: [ChartAxisLabel(text: "Axis title", settings: labelSettings)], axisValuesGenerator: xGenerator, labelsGenerator: labelsGenerator) 32 | 33 | let yModel = ChartAxisModel(firstModelValue: 0, lastModelValue: 16, axisTitleLabels: [ChartAxisLabel(text: "Axis title", settings: labelSettings.defaultVertical())], axisValuesGenerator: generator, labelsGenerator: labelsGenerator) 34 | 35 | let chartFrame = ExamplesDefaults.chartFrame(view.bounds) 36 | 37 | let chartSettings = ExamplesDefaults.chartSettingsWithPanZoom 38 | 39 | // generate axes layers and calculate chart inner frame, based on the axis models 40 | let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel) 41 | let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame) 42 | 43 | // create layer with guidelines 44 | let guidelinesLayerSettings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.black, linesWidth: ExamplesDefaults.guidelinesWidth) 45 | let guidelinesLayer = ChartGuideLinesDottedLayer(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, settings: guidelinesLayerSettings) 46 | 47 | // view generator - this is a function that creates a view for each chartpoint 48 | let viewGenerator = {(chartPointModel: ChartPointLayerModel, layer: ChartPointsViewsLayer, chart: Chart) -> UIView? in 49 | let viewSize: CGFloat = Env.iPad ? 30 : 20 50 | let center = chartPointModel.screenLoc 51 | let label = UILabel(frame: CGRect(x: center.x - viewSize / 2, y: center.y - viewSize / 2, width: viewSize, height: viewSize)) 52 | label.backgroundColor = UIColor.green 53 | label.textAlignment = NSTextAlignment.center 54 | label.text = chartPointModel.chartPoint.y.description 55 | label.font = ExamplesDefaults.labelFont 56 | return label 57 | } 58 | 59 | // create layer that uses viewGenerator to display chartpoints 60 | let chartPointsLayer = ChartPointsViewsLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, chartPoints: chartPoints, viewGenerator: viewGenerator, mode: .translate) 61 | 62 | // create chart instance with frame and layers 63 | let chart = Chart( 64 | frame: chartFrame, 65 | innerFrame: innerFrame, 66 | settings: chartSettings, 67 | layers: [ 68 | xAxisLayer, 69 | yAxisLayer, 70 | guidelinesLayer, 71 | chartPointsLayer 72 | ] 73 | ) 74 | 75 | view.addSubview(chart.view) 76 | self.chart = chart 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Examples/Examples/MultipleLabelsExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultipleLabelsExample.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 04/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftCharts 11 | 12 | class MultipleLabelsExample: UIViewController { 13 | 14 | fileprivate var chart: Chart? // arc 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont) 20 | 21 | let cp1 = ChartPoint(x: MyMultiLabelAxisValue(myVal: 2), y: ChartAxisValueDouble(2)) 22 | let cp2 = ChartPoint(x: MyMultiLabelAxisValue(myVal: 4), y: ChartAxisValueDouble(6)) 23 | let cp3 = ChartPoint(x: MyMultiLabelAxisValue(myVal: 5), y: ChartAxisValueDouble(12)) 24 | let cp4 = ChartPoint(x: MyMultiLabelAxisValue(myVal: 8), y: ChartAxisValueDouble(4)) 25 | 26 | let chartPoints = [cp1, cp2, cp3, cp4] 27 | 28 | let xValues = chartPoints.map{$0.x} 29 | let yValues = ChartAxisValuesStaticGenerator.generateYAxisValuesWithChartPoints(chartPoints, minSegmentCount: 10, maxSegmentCount: 20, multiple: 2, axisValueGenerator: {ChartAxisValueDouble($0, labelSettings: labelSettings)}, addPaddingSegmentIfEdge: false) 30 | 31 | let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings)) 32 | let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings.defaultVertical())) 33 | let chartFrame = ExamplesDefaults.chartFrame(view.bounds) 34 | var chartSettings = ExamplesDefaults.chartSettingsWithPanZoom 35 | chartSettings.trailing = 20 36 | let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel) 37 | let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame) 38 | 39 | let lineModel = ChartLineModel(chartPoints: chartPoints, lineColor: UIColor.red, lineWidth: 1, animDuration: 1, animDelay: 0) 40 | let chartPointsLineLayer = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel]) 41 | 42 | let settings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.black, linesWidth: ExamplesDefaults.guidelinesWidth) 43 | let guidelinesLayer = ChartGuideLinesDottedLayer(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, settings: settings) 44 | 45 | let chart = Chart( 46 | frame: chartFrame, 47 | innerFrame: innerFrame, 48 | settings: chartSettings, 49 | layers: [ 50 | xAxisLayer, 51 | yAxisLayer, 52 | guidelinesLayer, 53 | chartPointsLineLayer 54 | ] 55 | ) 56 | 57 | view.addSubview(chart.view) 58 | self.chart = chart 59 | } 60 | } 61 | 62 | private class MyMultiLabelAxisValue: ChartAxisValue { 63 | 64 | fileprivate let myVal: Int 65 | fileprivate let derivedVal: Double 66 | 67 | init(myVal: Int) { 68 | self.myVal = myVal 69 | self.derivedVal = Double(myVal) / 5.0 70 | super.init(scalar: Double(myVal)) 71 | } 72 | 73 | override var labels:[ChartAxisLabel] { 74 | return [ 75 | ChartAxisLabel(text: "\(myVal)", settings: ChartLabelSettings(font: UIFont.systemFont(ofSize: 18), fontColor: UIColor.black)), 76 | ChartAxisLabel(text: "blabla", settings: ChartLabelSettings(font: UIFont.systemFont(ofSize: 10), fontColor: UIColor.blue)), 77 | ChartAxisLabel(text: "\(derivedVal)", settings: ChartLabelSettings(font: UIFont.systemFont(ofSize: 14), fontColor: UIColor.purple)) 78 | ] 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /Examples/Examples/TargetExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TargetExample.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 04/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftCharts 11 | 12 | class TargetExample: UIViewController { 13 | 14 | fileprivate var chart: Chart? // arc 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont) 20 | 21 | let chartPoints: [ChartPoint] = [(2, 2), (4, 4), (7, 1), (8, 11), (12, 3)].map{ChartPoint(x: ChartAxisValueInt($0.0, labelSettings: labelSettings), y: ChartAxisValueInt($0.1))} 22 | 23 | let xValues = chartPoints.map{$0.x} 24 | 25 | let yValues = ChartAxisValuesStaticGenerator.generateYAxisValuesWithChartPoints(chartPoints, minSegmentCount: 10, maxSegmentCount: 20, multiple: 2, axisValueGenerator: {ChartAxisValueDouble($0, labelSettings: labelSettings)}, addPaddingSegmentIfEdge: false) 26 | 27 | let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings)) 28 | let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings.defaultVertical())) 29 | let chartFrame = ExamplesDefaults.chartFrame(view.bounds) 30 | 31 | let chartSettings = ExamplesDefaults.chartSettingsWithPanZoom 32 | 33 | let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel) 34 | let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame) 35 | 36 | let lineModel = ChartLineModel(chartPoints: chartPoints, lineColor: UIColor.red, animDuration: 0.5, animDelay: 0) 37 | 38 | let targetGenerator = {(chartPointModel: ChartPointLayerModel, layer: ChartPointsLayer, chart: Chart) -> UIView? in 39 | if chartPointModel.index != 3 { 40 | return nil 41 | } 42 | return ChartPointTargetingView(chartPoint: chartPointModel.chartPoint, screenLoc: chartPointModel.screenLoc, animDuration: 0.5, animDelay: 1, layer: layer, chart: chart) 43 | } 44 | 45 | let chartPointsTargetLayer = ChartPointsViewsLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, chartPoints: chartPoints, viewGenerator: targetGenerator) 46 | 47 | let chartPointsLineLayer = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel]) 48 | 49 | let settings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.black, linesWidth: ExamplesDefaults.guidelinesWidth) 50 | let guidelinesLayer = ChartGuideLinesDottedLayer(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, settings: settings) 51 | 52 | let chart = Chart( 53 | frame: chartFrame, 54 | innerFrame: innerFrame, 55 | settings: chartSettings, 56 | layers: [ 57 | xAxisLayer, 58 | yAxisLayer, 59 | guidelinesLayer, 60 | chartPointsLineLayer, 61 | chartPointsTargetLayer 62 | ] 63 | ) 64 | 65 | view.addSubview(chart.view) 66 | self.chart = chart 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Examples/Examples/TrendlineExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrendlineExample.swift 3 | // Examples 4 | // 5 | // Created by ischuetz on 03/08/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftCharts 11 | 12 | class TrendlineExample: UIViewController { 13 | 14 | fileprivate var chart: Chart? // arc 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | let labelSettings = ChartLabelSettings(font: ExamplesDefaults.labelFont) 20 | 21 | let chartPoints: [ChartPoint] = [(1, 3), (2, 5), (3, 7.5), (4, 10), (5, 6), (6, 12)].map{ChartPoint(x: ChartAxisValueDouble($0.0, labelSettings: labelSettings), y: ChartAxisValueDouble($0.1))} 22 | 23 | let xValues = chartPoints.map{$0.x} 24 | let yValues = ChartAxisValuesStaticGenerator.generateYAxisValuesWithChartPoints(chartPoints, minSegmentCount: 10, maxSegmentCount: 20, multiple: 2, axisValueGenerator: {ChartAxisValueDouble($0, labelSettings: labelSettings)}, addPaddingSegmentIfEdge: false) 25 | 26 | let lineModel = ChartLineModel(chartPoints: chartPoints, lineColor: UIColor.red, animDuration: 1, animDelay: 0) 27 | 28 | let trendLineModel = ChartLineModel(chartPoints: TrendlineGenerator.trendline(chartPoints), lineColor: UIColor.blue, animDuration: 0.5, animDelay: 1) 29 | 30 | let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings)) 31 | let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: "Axis title", settings: labelSettings.defaultVertical())) 32 | let chartFrame = ExamplesDefaults.chartFrame(view.bounds) 33 | 34 | let chartSettings = ExamplesDefaults.chartSettingsWithPanZoom 35 | 36 | let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartSettings, chartFrame: chartFrame, xModel: xModel, yModel: yModel) 37 | let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame) 38 | 39 | let chartPointsLineLayer = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel]) 40 | 41 | let trendLineLayer = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [trendLineModel]) 42 | 43 | let settings = ChartGuideLinesDottedLayerSettings(linesColor: UIColor.black, linesWidth: ExamplesDefaults.guidelinesWidth) 44 | let guidelinesLayer = ChartGuideLinesDottedLayer(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, settings: settings) 45 | 46 | let chart = Chart( 47 | frame: chartFrame, 48 | innerFrame: innerFrame, 49 | settings: chartSettings, 50 | layers: [ 51 | xAxisLayer, 52 | yAxisLayer, 53 | guidelinesLayer, 54 | chartPointsLineLayer, 55 | trendLineLayer 56 | ] 57 | ) 58 | 59 | view.addSubview(chart.view) 60 | self.chart = chart 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Examples/HorizontalGuide.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HorizontalGuide.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 14/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Debug helper 12 | class HorizontalGuide: UIView { 13 | 14 | init(_ location: CGFloat, color: UIColor = UIColor.red) { 15 | super.init(frame: CGRect(x: -10000000, y: location, width: 100000000, height: 1)) 16 | backgroundColor = color 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | } -------------------------------------------------------------------------------- /Examples/Interface/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | }, 6 | "images" : [ 7 | { 8 | "filename" : "Icon-40.png", 9 | "size" : "40x40", 10 | "idiom" : "ipad", 11 | "scale" : "1x" 12 | }, 13 | { 14 | "filename" : "Icon-40@2x.png", 15 | "size" : "40x40", 16 | "idiom" : "ipad", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "filename" : "Icon-60@2x.png", 21 | "size" : "60x60", 22 | "idiom" : "iphone", 23 | "scale" : "2x" 24 | }, 25 | { 26 | "filename" : "Icon-72.png", 27 | "size" : "72x72", 28 | "idiom" : "ipad", 29 | "scale" : "1x" 30 | }, 31 | { 32 | "filename" : "Icon-72@2x.png", 33 | "size" : "72x72", 34 | "idiom" : "ipad", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "filename" : "Icon-76.png", 39 | "size" : "76x76", 40 | "idiom" : "ipad", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "filename" : "Icon-76@2x.png", 45 | "size" : "76x76", 46 | "idiom" : "ipad", 47 | "scale" : "2x" 48 | }, 49 | { 50 | "filename" : "Icon-Small-50.png", 51 | "size" : "50x50", 52 | "idiom" : "ipad", 53 | "scale" : "1x" 54 | }, 55 | { 56 | "filename" : "Icon-Small-50@2x.png", 57 | "size" : "50x50", 58 | "idiom" : "ipad", 59 | "scale" : "2x" 60 | }, 61 | { 62 | "filename" : "Icon-Small.png", 63 | "size" : "29x29", 64 | "idiom" : "iphone", 65 | "scale" : "1x" 66 | }, 67 | { 68 | "filename" : "Icon-Small@2x.png", 69 | "size" : "29x29", 70 | "idiom" : "iphone", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "filename" : "Icon.png", 75 | "size" : "57x57", 76 | "idiom" : "iphone", 77 | "scale" : "1x" 78 | }, 79 | { 80 | "filename" : "Icon@2x.png", 81 | "size" : "57x57", 82 | "idiom" : "iphone", 83 | "scale" : "2x" 84 | }, 85 | { 86 | "filename" : "Icon-Small@3x.png", 87 | "size" : "29x29", 88 | "idiom" : "iphone", 89 | "scale" : "3x" 90 | }, 91 | { 92 | "filename" : "Icon-40@3x.png", 93 | "size" : "40x40", 94 | "idiom" : "iphone", 95 | "scale" : "3x" 96 | }, 97 | { 98 | "filename" : "Icon-60@3x.png", 99 | "size" : "60x60", 100 | "idiom" : "iphone", 101 | "scale" : "3x" 102 | }, 103 | { 104 | "filename" : "Icon-40@2x.png", 105 | "size" : "40x40", 106 | "idiom" : "iphone", 107 | "scale" : "2x" 108 | }, 109 | { 110 | "filename" : "Icon-Small.png", 111 | "size" : "29x29", 112 | "idiom" : "ipad", 113 | "scale" : "1x" 114 | }, 115 | { 116 | "filename" : "Icon-Small@2x.png", 117 | "size" : "29x29", 118 | "idiom" : "ipad", 119 | "scale" : "2x" 120 | }, 121 | { 122 | "filename" : "Icon-83.5@2x.png", 123 | "size" : "83.5x83.5", 124 | "idiom" : "ipad", 125 | "scale" : "2x" 126 | }, 127 | { 128 | "filename" : "NotificationIcon@2x.png", 129 | "size" : "20x20", 130 | "idiom" : "iphone", 131 | "scale" : "2x" 132 | }, 133 | { 134 | "filename" : "NotificationIcon@3x.png", 135 | "size" : "20x20", 136 | "idiom" : "iphone", 137 | "scale" : "3x" 138 | }, 139 | { 140 | "filename" : "NotificationIcon~ipad.png", 141 | "size" : "20x20", 142 | "idiom" : "ipad", 143 | "scale" : "1x" 144 | }, 145 | { 146 | "filename" : "NotificationIcon~ipad@2x.png", 147 | "size" : "20x20", 148 | "idiom" : "ipad", 149 | "scale" : "2x" 150 | }, 151 | { 152 | "filename" : "ios-marketing.png", 153 | "size" : "1024x1024", 154 | "idiom" : "ios-marketing", 155 | "scale" : "1x" 156 | } 157 | ] 158 | } -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-72.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/Icon@2x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/NotificationIcon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/NotificationIcon@2x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/NotificationIcon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/NotificationIcon@3x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/NotificationIcon~ipad@2x.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/AppIcon.appiconset/ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Examples/Interface/Images.xcassets/AppIcon.appiconset/ios-marketing.png -------------------------------------------------------------------------------- /Examples/Interface/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "8.0", 8 | "subtype" : "736h", 9 | "scale" : "3x" 10 | }, 11 | { 12 | "orientation" : "landscape", 13 | "idiom" : "iphone", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "8.0", 16 | "subtype" : "736h", 17 | "scale" : "3x" 18 | }, 19 | { 20 | "orientation" : "portrait", 21 | "idiom" : "iphone", 22 | "extent" : "full-screen", 23 | "minimum-system-version" : "8.0", 24 | "subtype" : "667h", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "orientation" : "portrait", 29 | "idiom" : "iphone", 30 | "extent" : "full-screen", 31 | "minimum-system-version" : "7.0", 32 | "scale" : "2x" 33 | }, 34 | { 35 | "orientation" : "portrait", 36 | "idiom" : "iphone", 37 | "extent" : "full-screen", 38 | "minimum-system-version" : "7.0", 39 | "subtype" : "retina4", 40 | "scale" : "2x" 41 | }, 42 | { 43 | "orientation" : "portrait", 44 | "idiom" : "ipad", 45 | "extent" : "full-screen", 46 | "minimum-system-version" : "7.0", 47 | "scale" : "1x" 48 | }, 49 | { 50 | "orientation" : "landscape", 51 | "idiom" : "ipad", 52 | "extent" : "full-screen", 53 | "minimum-system-version" : "7.0", 54 | "scale" : "1x" 55 | }, 56 | { 57 | "orientation" : "portrait", 58 | "idiom" : "ipad", 59 | "extent" : "full-screen", 60 | "minimum-system-version" : "7.0", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "orientation" : "landscape", 65 | "idiom" : "ipad", 66 | "extent" : "full-screen", 67 | "minimum-system-version" : "7.0", 68 | "scale" : "2x" 69 | }, 70 | { 71 | "orientation" : "portrait", 72 | "idiom" : "iphone", 73 | "extent" : "full-screen", 74 | "scale" : "1x" 75 | }, 76 | { 77 | "orientation" : "portrait", 78 | "idiom" : "iphone", 79 | "extent" : "full-screen", 80 | "scale" : "2x" 81 | }, 82 | { 83 | "orientation" : "portrait", 84 | "idiom" : "iphone", 85 | "extent" : "full-screen", 86 | "subtype" : "retina4", 87 | "scale" : "2x" 88 | }, 89 | { 90 | "orientation" : "portrait", 91 | "idiom" : "ipad", 92 | "extent" : "to-status-bar", 93 | "scale" : "1x" 94 | }, 95 | { 96 | "orientation" : "portrait", 97 | "idiom" : "ipad", 98 | "extent" : "full-screen", 99 | "scale" : "1x" 100 | }, 101 | { 102 | "orientation" : "landscape", 103 | "idiom" : "ipad", 104 | "extent" : "to-status-bar", 105 | "scale" : "1x" 106 | }, 107 | { 108 | "orientation" : "landscape", 109 | "idiom" : "ipad", 110 | "extent" : "full-screen", 111 | "scale" : "1x" 112 | }, 113 | { 114 | "orientation" : "portrait", 115 | "idiom" : "ipad", 116 | "extent" : "to-status-bar", 117 | "scale" : "2x" 118 | }, 119 | { 120 | "orientation" : "portrait", 121 | "idiom" : "ipad", 122 | "extent" : "full-screen", 123 | "scale" : "2x" 124 | }, 125 | { 126 | "orientation" : "landscape", 127 | "idiom" : "ipad", 128 | "extent" : "to-status-bar", 129 | "scale" : "2x" 130 | }, 131 | { 132 | "orientation" : "landscape", 133 | "idiom" : "ipad", 134 | "extent" : "full-screen", 135 | "scale" : "2x" 136 | } 137 | ], 138 | "info" : { 139 | "version" : 1, 140 | "author" : "xcode" 141 | } 142 | } -------------------------------------------------------------------------------- /Examples/PointGuide.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PointGuide.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 19/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Debug helper 12 | class PointGuide: UIView { 13 | 14 | let size: CGFloat = 5 15 | 16 | init(_ location: CGPoint, color: UIColor = UIColor.red) { 17 | super.init(frame: CGRect(x: location.x - size / 2, y: location.y - size / 2, width: size, height: size)) 18 | backgroundColor = color 19 | } 20 | 21 | required init?(coder aDecoder: NSCoder) { 22 | fatalError("init(coder:) has not been implemented") 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Examples/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 13/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension String { 12 | 13 | func size(_ font: UIFont) -> CGSize { 14 | return NSAttributedString(string: self, attributes: [.font: font]).size() 15 | } 16 | 17 | func width(_ font: UIFont) -> CGFloat { 18 | return size(font).width 19 | } 20 | 21 | func height(_ font: UIFont) -> CGFloat { 22 | return size(font).height 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Examples/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | SwiftCharts 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIMainStoryboardFile~iphone 32 | Main_iPhone 33 | UIRequiredDeviceCapabilities 34 | 35 | armv7 36 | 37 | UIStatusBarTintParameters 38 | 39 | UINavigationBar 40 | 41 | Style 42 | UIBarStyleDefault 43 | Translucent 44 | 45 | 46 | 47 | UISupportedInterfaceOrientations 48 | 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | UIInterfaceOrientationPortrait 52 | UIInterfaceOrientationPortraitUpsideDown 53 | 54 | UISupportedInterfaceOrientations~ipad 55 | 56 | UIInterfaceOrientationPortrait 57 | UIInterfaceOrientationPortraitUpsideDown 58 | UIInterfaceOrientationLandscapeLeft 59 | UIInterfaceOrientationLandscapeRight 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Examples/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // SwiftCharts 4 | // 5 | // Created by Ivan Schuetz on 17/02/2017. 6 | // Copyright © 2017 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | 13 | public convenience init(hexString: String) { 14 | let hexString = hexString.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) as String 15 | let scanner = Scanner(string: hexString) 16 | 17 | if (hexString.hasPrefix("#")) { 18 | scanner.scanLocation = 1 19 | } 20 | 21 | var color: UInt32 = 0 22 | scanner.scanHexInt32(&color) 23 | 24 | let mask = 0x000000FF 25 | let r = Int(color >> 16) & mask 26 | let g = Int(color >> 8) & mask 27 | let b = Int(color) & mask 28 | 29 | let red = CGFloat(r) / 255.0 30 | let green = CGFloat(g) / 255.0 31 | let blue = CGFloat(b) / 255.0 32 | 33 | self.init(red:red, green:green, blue:blue, alpha:1) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Examples/VerticalGuide.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VerticalGuide.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 14/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Debug helper 12 | class VerticalGuide: UIView { 13 | 14 | init(_ location: CGFloat, color: UIColor = UIColor.red) { 15 | super.init(frame: CGRect(x: location, y: -10000000, width: 1, height: 100000000)) 16 | backgroundColor = color 17 | } 18 | 19 | required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | } -------------------------------------------------------------------------------- /Issue_template.md: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "SwiftCharts", 6 | platforms: [ 7 | .iOS(.v9) 8 | ], 9 | products: [ 10 | .library(name: "SwiftCharts", type: .dynamic, targets: ["SwiftCharts"]) 11 | ], 12 | targets: [ 13 | .target( 14 | name: "SwiftCharts", 15 | path: "SwiftCharts" 16 | ) 17 | ], 18 | swiftLanguageVersions: [.v5] 19 | ) 20 | -------------------------------------------------------------------------------- /Screenshots/IMG_0022.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0022.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0023.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0023.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0024.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0024.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0026.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0026.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0027.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0027.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0029.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0029.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0033.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0033.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0034.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0034.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0037.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0037.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0039.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0039.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0040.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0040.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0041.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0041.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0101.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0101.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_0102.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_0102.jpeg -------------------------------------------------------------------------------- /Screenshots/IMG_1328.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_1328.PNG -------------------------------------------------------------------------------- /Screenshots/IMG_1330.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_1330.PNG -------------------------------------------------------------------------------- /Screenshots/IMG_1332.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_1332.PNG -------------------------------------------------------------------------------- /Screenshots/IMG_1334.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/IMG_1334.PNG -------------------------------------------------------------------------------- /Screenshots/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/layers.png -------------------------------------------------------------------------------- /Screenshots/multi-chart-touch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/multi-chart-touch.jpg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0022.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0022.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0023.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0023.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0024.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0024.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0025.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0025.png -------------------------------------------------------------------------------- /Screenshots/small/IMG_0026.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0026.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0027.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0027.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0028.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0028.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0029.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0029.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0031.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0031.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0033.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0033.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0034.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0034.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0037.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0037.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0038.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0038.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0039.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0039.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0040.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0040.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0041.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0041.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0101.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0101.jpeg -------------------------------------------------------------------------------- /Screenshots/small/IMG_0102.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivnsch/SwiftCharts/c354c1945bb35a1f01b665b22474f6db28cba4a2/Screenshots/small/IMG_0102.jpeg -------------------------------------------------------------------------------- /SwiftCharts.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "SwiftCharts" 3 | s.version = "0.6.5" 4 | s.summary = "Easy to use and highly customizable charts library for iOS" 5 | s.homepage = "https://github.com/i-schuetz/SwiftCharts" 6 | s.license = { :type => "Apache License, Version 2.0", :file => "LICENSE" } 7 | s.authors = { "Ivan Schuetz" => "ivanschuetz@gmail.com"} 8 | s.ios.deployment_target = "8.0" 9 | s.tvos.deployment_target = "9.0" 10 | s.source = { :git => "https://github.com/i-schuetz/SwiftCharts.git", :tag => '0.6.5'} 11 | s.source_files = 'SwiftCharts/*.swift', 'SwiftCharts/**/*.swift' 12 | s.frameworks = "Foundation", "UIKit", "CoreGraphics" 13 | s.swift_version = '5.0' 14 | end 15 | -------------------------------------------------------------------------------- /SwiftCharts.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftCharts.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftCharts.xcodeproj/xcshareddata/xcschemes/Examples.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /SwiftCharts.xcodeproj/xcshareddata/xcschemes/SwiftCharts tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /SwiftCharts/Array_String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array_String.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 13/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol StringType { 12 | func width(_ font: UIFont) -> CGFloat 13 | func height(_ font: UIFont) -> CGFloat 14 | } 15 | 16 | extension String: StringType {} 17 | 18 | extension Array where Element: StringType { 19 | 20 | func maxWidth(_ font: UIFont) -> CGFloat { 21 | return reduce(0) {sum, str in 22 | sum + str.width(font) 23 | } 24 | } 25 | 26 | func maxHeight(_ font: UIFont) -> CGFloat { 27 | return reduce(0) {sum, str in 28 | sum + str.width(font) 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLabel.swift 3 | // swift_charts 4 | // 5 | // Created by ischuetz on 01/03/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// A model of an axis label 12 | open class ChartAxisLabel { 13 | 14 | /// Displayed text. Can be truncated. 15 | public let text: String 16 | 17 | public let settings: ChartLabelSettings 18 | 19 | open fileprivate(set) var originalText: String 20 | 21 | var hidden: Bool = false 22 | 23 | open lazy private(set) var textSizeNonRotated: CGSize = { 24 | return self.text.size(self.settings.font) 25 | }() 26 | 27 | /// The size of the bounding rectangle for the axis label, taking into account the font and rotation it will be drawn with 28 | open lazy private(set) var textSize: CGSize = { 29 | let size = self.textSizeNonRotated 30 | if self.settings.rotation =~ 0 { 31 | return size 32 | } else { 33 | return CGRect(x: 0, y: 0, width: size.width, height: size.height).boundingRectAfterRotating(radians: self.settings.rotation * CGFloat.pi / 180.0).size 34 | } 35 | }() 36 | 37 | public init(text: String, settings: ChartLabelSettings) { 38 | self.text = text 39 | self.settings = settings 40 | self.originalText = text 41 | } 42 | 43 | func copy(_ text: String? = nil, settings: ChartLabelSettings? = nil, originalText: String? = nil, hidden: Bool? = nil) -> ChartAxisLabel { 44 | let label = ChartAxisLabel( 45 | text: text ?? self.text, 46 | settings: settings ?? self.settings 47 | ) 48 | label.originalText = originalText ?? self.originalText 49 | label.hidden = hidden ?? self.hidden 50 | return label 51 | } 52 | } 53 | 54 | extension ChartAxisLabel: CustomDebugStringConvertible { 55 | public var debugDescription: String { 56 | return [ 57 | "text": text, 58 | "settings": settings 59 | ] 60 | .debugDescription 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLabelGeneratorDate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLabelGeneratorDate.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 05/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartAxisLabelsGeneratorDate: ChartAxisLabelsGeneratorBase { 12 | 13 | public let labelSettings: ChartLabelSettings 14 | 15 | public let formatter: DateFormatter 16 | 17 | public init(labelSettings: ChartLabelSettings, formatter: DateFormatter = ChartAxisLabelsGeneratorDate.defaultFormatter) { 18 | self.labelSettings = labelSettings 19 | self.formatter = formatter 20 | } 21 | 22 | open override func generate(_ scalar: Double) -> [ChartAxisLabel] { 23 | let text = formatter.string(from: Date(timeIntervalSince1970: scalar)) 24 | return [ChartAxisLabel(text: text, settings: labelSettings)] 25 | } 26 | 27 | public static var defaultFormatter: DateFormatter = { 28 | let formatter = DateFormatter() 29 | formatter.dateFormat = "dd.MM.yyyy" 30 | return formatter 31 | }() 32 | 33 | open override func fonts(_ scalar: Double) -> [UIFont] { 34 | return [labelSettings.font] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLabelsConflictSolver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLabelsConflictSolver.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 27/06/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Maps label drawers array to a new array in order to solve conflicts 12 | public protocol ChartAxisLabelsConflictSolver { 13 | 14 | func solveConflicts(_ labels: [ChartAxisValueLabelDrawers]) -> [ChartAxisValueLabelDrawers] 15 | } 16 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLabelsConflictSolverMoveUpDown.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLabelsConflictSolverMoveUpDown.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 27/06/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreGraphics 11 | 12 | /// Solves frame overlaps by moving drawers up and down by half of the height of their frames. Assumes being used for y axis, which currently supports only 1 label per axis value. 13 | open class ChartAxisLabelsConflictSolverMoveUpDown: ChartAxisLabelsConflictSolver { 14 | 15 | let maxIterations: Int 16 | 17 | /** 18 | - parameter maxIterations: Max count of iterations through the passed labels to solve conflicts. 19 | */ 20 | public init(maxIterations: Int = 20) { 21 | self.maxIterations = maxIterations 22 | } 23 | 24 | open func solveConflicts(_ labels: [ChartAxisValueLabelDrawers]) -> [ChartAxisValueLabelDrawers] { 25 | 26 | var lastDrawerWithRect: (drawer: ChartLabelDrawer, rect: CGRect)? 27 | 28 | var iteration = 0 29 | var doNextIteration = true 30 | 31 | while (iteration < maxIterations && doNextIteration) { 32 | 33 | doNextIteration = false 34 | 35 | for (_, labelDrawers) in labels { 36 | 37 | guard let labelDrawer = labelDrawers.first else {continue} // for now y axis supports only one label / value 38 | 39 | if let (lastDrawer, lastRect) = lastDrawerWithRect { 40 | let intersection = labelDrawer.frame.intersection(lastRect) 41 | if intersection != CGRect.null { 42 | 43 | doNextIteration = true // if there's a conflict do another iteration after this iteration in case there are more conflicts as result of solving this conflict 44 | 45 | labelDrawer.screenLoc = CGPoint(x: labelDrawer.screenLoc.x, y: labelDrawer.screenLoc.y - intersection.height / 2) 46 | lastDrawer.screenLoc = CGPoint(x: lastDrawer.screenLoc.x, y: lastDrawer.screenLoc.y + intersection.height / 2) 47 | } 48 | } 49 | 50 | lastDrawerWithRect = (labelDrawer, labelDrawer.frame) 51 | } 52 | 53 | iteration += 1 54 | } 55 | 56 | return labels 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLabelsGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLabelsGenerator.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 27/06/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreGraphics 11 | import UIKit 12 | 13 | /// Generates labels for an axis value. Note: Supports only one label per axis value (1 element array) 14 | public protocol ChartAxisLabelsGenerator { 15 | 16 | /// If the complete label should disappear as soon as a part of it is outside of the axis edges 17 | var onlyShowCompleteLabels: Bool {get set} 18 | 19 | var maxStringPTWidth: CGFloat? {get set} 20 | 21 | func generate(_ scalar: Double) -> [ChartAxisLabel] 22 | 23 | /// Generates label for scalar taking into account axis state 24 | func generate(_ scalar: Double, axis: ChartAxis) -> [ChartAxisLabel] 25 | 26 | func fonts(_ scalar: Double) -> [UIFont] 27 | 28 | func cache(_ scalar: Double, labels: [ChartAxisLabel]) 29 | 30 | func cachedLabels(_ scalar: Double) -> [ChartAxisLabel]? 31 | } 32 | 33 | 34 | extension ChartAxisLabelsGenerator { 35 | 36 | public var onlyShowCompleteLabels: Bool { 37 | return true 38 | } 39 | 40 | public var maxStringPTWidth: CGFloat? { 41 | return nil 42 | } 43 | 44 | fileprivate func truncate(_ labels: [ChartAxisLabel], scalar: Double, maxStringPTWidth: CGFloat) -> [ChartAxisLabel] { 45 | guard let font = fonts(scalar).first else {return []} 46 | return labels.map {label in 47 | label.copy(label.text.truncate(maxStringPTWidth, font: font)) 48 | } 49 | } 50 | 51 | public func generate(_ scalar: Double, axis: ChartAxis) -> [ChartAxisLabel] { 52 | 53 | return cachedLabels(scalar) ?? { 54 | 55 | let labels = generate(scalar) 56 | 57 | let truncatedLabels: [ChartAxisLabel] = maxStringPTWidth.map{truncate(labels, scalar: scalar, maxStringPTWidth: $0)} ?? labels 58 | cache(scalar, labels: truncatedLabels) 59 | 60 | if onlyShowCompleteLabels { 61 | return truncatedLabels.first.map {label in 62 | if axis.isInBoundaries(axis.screenLocForScalar(scalar), screenSize: label.textSize) { 63 | return [label] 64 | } else { 65 | return [] 66 | } 67 | } ?? [] 68 | } else { 69 | return truncatedLabels 70 | } 71 | }() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLabelsGeneratorBase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLabelsGeneratorBase.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 17/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreGraphics 11 | import UIKit 12 | 13 | /// Needed for common stored properties which are not possible in the extension (without workarounds) 14 | open class ChartAxisLabelsGeneratorBase: ChartAxisLabelsGenerator { 15 | 16 | open var onlyShowCompleteLabels: Bool = false 17 | 18 | open var maxStringPTWidth: CGFloat? = nil 19 | 20 | var cache = [Double: [ChartAxisLabel]]() 21 | 22 | open func generate(_ scalar: Double) -> [ChartAxisLabel] { 23 | fatalError("Override") 24 | } 25 | 26 | open func fonts(_ scalar: Double) -> [UIFont] { 27 | fatalError("Override") 28 | } 29 | 30 | open func cache(_ scalar: Double, labels: [ChartAxisLabel]) { 31 | cache[scalar] = labels 32 | } 33 | 34 | open func cachedLabels(_ scalar: Double) -> [ChartAxisLabel]? { 35 | return cache[scalar] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLabelsGeneratorBasic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLabelsGeneratorBasic.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 27/06/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | /// Generates a single unformatted label for scalar 13 | open class ChartAxisLabelsGeneratorBasic: ChartAxisLabelsGeneratorBase { 14 | 15 | public let labelSettings: ChartLabelSettings 16 | 17 | public init(labelSettings: ChartLabelSettings) { 18 | self.labelSettings = labelSettings 19 | } 20 | 21 | open override func generate(_ scalar: Double) -> [ChartAxisLabel] { 22 | return [ChartAxisLabel(text: "\(scalar)", settings: labelSettings)] 23 | } 24 | 25 | open override func fonts(_ scalar: Double) -> [UIFont] { 26 | return [labelSettings.font] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLabelsGeneratorFixed.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLabelsGeneratorFixed.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 27/06/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | open class ChartAxisLabelsGeneratorFixed: ChartAxisLabelsGeneratorBase { 13 | 14 | public let dict: [Double: [ChartAxisLabel]] 15 | 16 | public convenience init(axisValues: [ChartAxisValue]) { 17 | var dict = [Double: [ChartAxisLabel]]() 18 | for axisValue in axisValues { 19 | if !axisValue.hidden { 20 | dict[axisValue.scalar] = axisValue.labels 21 | } 22 | 23 | } 24 | self.init(dict: dict) 25 | } 26 | 27 | public init(dict: [Double: [ChartAxisLabel]]) { 28 | self.dict = dict 29 | } 30 | 31 | open override func generate(_ scalar: Double) -> [ChartAxisLabel] { 32 | return dict[scalar] ?? [] 33 | } 34 | 35 | open override func fonts(_ scalar: Double) -> [UIFont] { 36 | return dict[scalar].map {labels in labels.map{$0.settings.font}} ?? [] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLabelsGeneratorFunc.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLabelsGeneratorFunc.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 28/06/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | /// Label generator that delegates to a closure, for greater flexibility 13 | open class ChartAxisLabelsGeneratorFunc: ChartAxisLabelsGeneratorBase { 14 | 15 | let f: (Double) -> [ChartAxisLabel] 16 | 17 | /// Convenience initializer for function which maps scalar to a single label 18 | public convenience init(f: @escaping (Double) -> ChartAxisLabel) { 19 | self.init(f: {scalar in 20 | return [f(scalar)] 21 | }) 22 | } 23 | 24 | public init(f: @escaping (Double) -> [ChartAxisLabel]) { 25 | self.f = f 26 | } 27 | 28 | open override func generate(_ scalar: Double) -> [ChartAxisLabel] { 29 | return f(scalar) 30 | } 31 | 32 | open override func fonts(_ scalar: Double) -> [UIFont] { 33 | return f(scalar).map{$0.settings.font} 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLabelsGeneratorNumber.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLabelsGeneratorNumber.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 27/06/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | /// Generates a single formatted number for scalar 13 | open class ChartAxisLabelsGeneratorNumber: ChartAxisLabelsGeneratorBase { 14 | 15 | public let labelSettings: ChartLabelSettings 16 | 17 | public let formatter: NumberFormatter 18 | 19 | public init(labelSettings: ChartLabelSettings, formatter: NumberFormatter = ChartAxisLabelsGeneratorNumber.defaultFormatter) { 20 | self.labelSettings = labelSettings 21 | self.formatter = formatter 22 | } 23 | 24 | open override func generate(_ scalar: Double) -> [ChartAxisLabel] { 25 | let text = formatter.string(from: NSNumber(value: scalar))! 26 | return [ChartAxisLabel(text: text, settings: labelSettings)] 27 | } 28 | 29 | public static var defaultFormatter: NumberFormatter = { 30 | let formatter = NumberFormatter() 31 | formatter.maximumFractionDigits = 2 32 | return formatter 33 | }() 34 | 35 | open override func fonts(_ scalar: Double) -> [UIFont] { 36 | return [labelSettings.font] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLabelsGeneratorNumberSuffix.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLabelsGeneratorNumberSuffix.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 21/07/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public enum ChartAxisLabelNumberSuffixUnit { 13 | case k, m, g, t, p, e 14 | 15 | static var seq: [ChartAxisLabelNumberSuffixUnit] { 16 | return [k, m, g, t, p, e] 17 | } 18 | 19 | var values: (factor: Double, text: String) { 20 | switch self { 21 | case .k: return (pow(10, 3), "K") 22 | case .m: return (pow(10, 6), "M") 23 | case .g: return (pow(10, 9), "G") 24 | case .t: return (pow(10, 12), "T") 25 | case .p: return (pow(10, 15), "P") 26 | case .e: return (pow(10, 18), "E") 27 | } 28 | } 29 | 30 | public var factor: Double { 31 | return values.factor 32 | } 33 | 34 | public var text: String { 35 | return values.text 36 | } 37 | } 38 | 39 | open class ChartAxisLabelsGeneratorNumberSuffix: ChartAxisLabelsGeneratorBase { 40 | 41 | public let labelSettings: ChartLabelSettings 42 | 43 | public let startUnit: ChartAxisLabelNumberSuffixUnit 44 | 45 | public let formatter: NumberFormatter 46 | 47 | public init(labelSettings: ChartLabelSettings, startUnit: ChartAxisLabelNumberSuffixUnit = .m, formatter: NumberFormatter = ChartAxisLabelsGeneratorNumberSuffix.defaultFormatter) { 48 | self.labelSettings = labelSettings 49 | self.startUnit = startUnit 50 | self.formatter = formatter 51 | } 52 | 53 | // src: http://stackoverflow.com/a/23290016/930450 54 | open override func generate(_ scalar: Double) -> [ChartAxisLabel] { 55 | let sign = scalar < 0 ? "-" : "" 56 | 57 | let absScalar = fabs(scalar) 58 | 59 | let (number, suffix): (Double, String) = { 60 | if (absScalar < startUnit.factor) { 61 | return (absScalar, "") 62 | 63 | } else { 64 | let exp = Int(log10(absScalar) / 3) 65 | let roundedScalar = round(10 * absScalar / pow(1000, Double(exp))) / 10 66 | return (roundedScalar, ChartAxisLabelNumberSuffixUnit.seq[exp - 1].text) 67 | } 68 | }() 69 | 70 | let text = "\(sign)\(formatter.string(from: NSNumber(value: number))!)\(suffix)" 71 | return [ChartAxisLabel(text: text, settings: labelSettings)] 72 | } 73 | 74 | public static var defaultFormatter: NumberFormatter = { 75 | let formatter = NumberFormatter() 76 | formatter.maximumFractionDigits = 2 77 | return formatter 78 | }() 79 | 80 | open override func fonts(_ scalar: Double) -> [UIFont] { 81 | return [labelSettings.font] 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisLayer.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 02/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol ChartAxisLayer: ChartLayer { 12 | 13 | // Axis, used to map between model values and screen locations 14 | var axis: ChartAxis {get} 15 | 16 | var tapSettings: ChartAxisLayerTapSettings? {get set} 17 | 18 | var labelsGenerator: ChartAxisLabelsGenerator {get set} 19 | 20 | /// Displayed axis values 21 | var currentAxisValues: [Double] {get} 22 | 23 | /// The frame of the layer. This includes title, labels and line, and takes into account possible rotation and spacing settings. 24 | var frame: CGRect {get} 25 | 26 | var frameWithoutLabels: CGRect {get} 27 | 28 | /// Screen locations of current axis values 29 | var axisValuesScreenLocs: [CGFloat] {get} 30 | 31 | /// The axis values with their respective frames relative to the chart's view 32 | var axisValuesWithFrames: [(axisValue: Double, frames: [CGRect])] {get} 33 | 34 | /// The smallest screen distance between axis values 35 | var minAxisScreenSpace: CGFloat {get} 36 | 37 | /// Whether the axis is low (leading or bottom) or high (trailing or top) 38 | var low: Bool {get} 39 | 40 | var lineP1: CGPoint {get} 41 | var lineP2: CGPoint {get} 42 | 43 | /// If the axis frame should (incrementally) affect the inner frame size of the chart 44 | var canChangeFrameSize: Bool {set get} 45 | } 46 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisValuesGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValuesGenerator.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 27/06/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Generates axis values to be displayed 12 | public protocol ChartAxisValuesGenerator { 13 | 14 | var first: Double? {get} 15 | 16 | var last: Double? {get} 17 | 18 | func axisInitialized(_ axis: ChartAxis) 19 | 20 | func generate(_ axis: ChartAxis) -> [Double] 21 | } 22 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisValuesGeneratorDate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValuesGeneratorDate.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 05/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartAxisValuesGeneratorDate: ChartAxisGeneratorMultiplier { 12 | 13 | let unit: Calendar.Component 14 | 15 | fileprivate let minSpace: CGFloat 16 | fileprivate let preferredDividers: Int 17 | fileprivate let maxTextSize: CGFloat 18 | 19 | public init(unit: Calendar.Component, preferredDividers: Int, minSpace: CGFloat, maxTextSize: CGFloat, multiplierUpdateMode: ChartAxisGeneratorMultiplierUpdateMode = .halve) { 20 | self.unit = unit 21 | self.preferredDividers = preferredDividers 22 | self.minSpace = minSpace 23 | self.maxTextSize = maxTextSize 24 | super.init(0, multiplierUpdateMode: multiplierUpdateMode) 25 | } 26 | 27 | override func calculateModelStart(_ axis: ChartAxis, multiplier: Double) -> Double { 28 | guard !multiplier.isNaN && !multiplier.isZero else {return Double.nan} 29 | 30 | let firstVisibleDate = Date(timeIntervalSince1970: axis.firstVisible) 31 | let firstInitDate = Date(timeIntervalSince1970: axis.firstInit) 32 | 33 | return firstInitDate.addComponent(Int((Double(firstVisibleDate.timeInterval(firstInitDate, unit: unit)) / multiplier)) * Int(multiplier), unit: unit).timeIntervalSince1970 34 | } 35 | 36 | override func incrementScalar(_ scalar: Double, multiplier: Double) -> Double { 37 | let scalarDate = Date(timeIntervalSince1970: scalar) 38 | return scalarDate.addComponent(Int(multiplier), unit: unit).timeIntervalSince1970 39 | } 40 | 41 | open override func axisInitialized(_ axis: ChartAxis) { 42 | 43 | var dividers = preferredDividers 44 | var cont = true 45 | 46 | while dividers > 1 && cont { 47 | if requiredLengthForDividers(dividers) < axis.screenLength { 48 | 49 | let firstDate = Date(timeIntervalSince1970: axis.first) 50 | let lastDate = Date(timeIntervalSince1970: axis.last) 51 | let lengthInUnits = lastDate.timeInterval(firstDate, unit: unit) 52 | 53 | multiplier = Double(lengthInUnits) / Double(dividers) 54 | cont = false 55 | 56 | } else { 57 | dividers -= 1 58 | } 59 | } 60 | } 61 | 62 | fileprivate func requiredLengthForDividers(_ dividers: Int) -> CGFloat { 63 | return minSpace + ((maxTextSize + minSpace) * CGFloat(dividers)) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisValuesGeneratorFixed.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValuesGeneratorFixed.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 27/06/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Generates a fixed axis values array 12 | open class ChartAxisValuesGeneratorFixed: ChartAxisValuesGenerator { 13 | 14 | open var first: Double? { 15 | return values.first 16 | } 17 | 18 | open var last: Double? { 19 | return values.last 20 | } 21 | 22 | open internal(set) var values: [Double] 23 | 24 | public convenience init(values: [ChartAxisValue]) { 25 | self.init(values: values.map{$0.scalar}) 26 | } 27 | 28 | public init(values: [Double]) { 29 | self.values = values 30 | } 31 | 32 | open func axisInitialized(_ axis: ChartAxis) {} 33 | 34 | open func generate(_ axis: ChartAxis) -> [Double] { 35 | let (first, last) = axis.firstVisible > axis.lastVisible ? (axis.lastModelValueInBounds, axis.firstVisible) : (axis.firstVisible, axis.lastVisible) 36 | return values.filter{$0 >= first && $0 <= last} 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisValuesGeneratorFixedNonOverlapping.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValuesGeneratorFixedNonOverlapping.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz / Iain Bryson on 19/07/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartAxisValuesGeneratorFixedNonOverlapping: ChartAxisValuesGeneratorFixed { 12 | 13 | public let axisValues: [ChartAxisValue] 14 | 15 | public let maxLabelSize: CGSize 16 | public let totalLabelSize: CGSize 17 | public let spacing: CGFloat 18 | 19 | fileprivate var isX: Bool 20 | 21 | init(axisValues: [ChartAxisValue], spacing: CGFloat, isX: Bool) { 22 | self.axisValues = axisValues 23 | self.spacing = spacing 24 | self.isX = isX 25 | 26 | (totalLabelSize, maxLabelSize) = axisValues.calculateLabelsDimensions() 27 | 28 | super.init(values: axisValues.map{$0.scalar}) 29 | } 30 | 31 | open override func generate(_ axis: ChartAxis) -> [Double] { 32 | updateAxisValues(axis) 33 | return super.generate(axis) 34 | } 35 | 36 | fileprivate func updateAxisValues(_ axis: ChartAxis) { 37 | values = selectNonOverlappingAxisLabels(axisValues, screenLength: axis.screenLength).map{$0.scalar} 38 | } 39 | 40 | fileprivate func selectNonOverlappingAxisLabels(_ axisValues: [ChartAxisValue], screenLength: CGFloat) -> [ChartAxisValue] { 41 | 42 | // Select only the x-axis labels which would prevent any overlap 43 | let spacePerTick = screenLength / CGFloat(axisValues.count) 44 | 45 | var filteredAxisValues: [ChartAxisValue] = [] 46 | 47 | var coord: CGFloat = 0, currentLabelEnd: CGFloat = 0 48 | axisValues.forEach({axisValue in 49 | coord += spacePerTick 50 | if (currentLabelEnd <= coord) { 51 | filteredAxisValues.append(axisValue) 52 | currentLabelEnd = coord + (isX ? maxLabelSize.width : maxLabelSize.height) + spacing 53 | } 54 | }) 55 | 56 | // Always show the last label 57 | if let filteredLast = filteredAxisValues.last, let axisLabelLast = axisValues.last , filteredLast != axisLabelLast { 58 | filteredAxisValues[filteredAxisValues.count - 1] = axisLabelLast 59 | } 60 | 61 | return filteredAxisValues 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisValuesGeneratorNice.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValuesGeneratorNice.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 04/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartAxisValuesGeneratorNice: ChartAxisGeneratorMultiplier { 12 | 13 | open override var first: Double? { 14 | return Double(minValue) 15 | } 16 | 17 | open override var last: Double? { 18 | return Double(maxValue) 19 | } 20 | 21 | fileprivate var minValue: Double 22 | fileprivate var maxValue: Double 23 | fileprivate let minSpace: CGFloat 24 | fileprivate let preferredDividers: Int 25 | 26 | fileprivate let maxTextSize: CGFloat 27 | 28 | public init(minValue: Double, maxValue: Double, preferredDividers: Int, minSpace: CGFloat, maxTextSize: CGFloat, multiplierUpdateMode: ChartAxisGeneratorMultiplierUpdateMode = .halve) { 29 | 30 | self.minValue = minValue 31 | self.maxValue = maxValue 32 | 33 | self.preferredDividers = preferredDividers 34 | self.minSpace = minSpace 35 | 36 | self.maxTextSize = maxTextSize 37 | 38 | super.init(Double.greatestFiniteMagnitude, multiplierUpdateMode: multiplierUpdateMode) 39 | } 40 | 41 | func niceRangeAndMultiplier(_ dividers: Int) -> (minValue: Double, maxValue: Double, multiplier: Double) { 42 | let niceLength = ChartNiceNumberCalculator.niceNumber(maxValue - minValue, round: true) 43 | let niceMultiplier = ChartNiceNumberCalculator.niceNumber(niceLength / (Double(dividers) - 1), round: true) 44 | let niceMinValue = floor(minValue / niceMultiplier) * niceMultiplier 45 | let niceMaxValue = ceil(maxValue / niceMultiplier) * niceMultiplier 46 | return (niceMinValue, niceMaxValue, niceMultiplier) 47 | } 48 | 49 | open override func axisInitialized(_ axis: ChartAxis) { 50 | 51 | var dividers = preferredDividers 52 | var cont = true 53 | 54 | while dividers > 1 && cont { 55 | 56 | let nice = niceRangeAndMultiplier(dividers) 57 | 58 | if requiredLengthForDividers(dividers) < axis.screenLength { 59 | 60 | minValue = nice.minValue 61 | maxValue = nice.maxValue 62 | multiplier = nice.multiplier 63 | 64 | cont = false 65 | 66 | } else { 67 | dividers -= 1 68 | } 69 | } 70 | } 71 | 72 | fileprivate func requiredLengthForDividers(_ dividers: Int) -> CGFloat { 73 | return minSpace + ((maxTextSize + minSpace) * CGFloat(dividers)) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisValuesGeneratorXFixedNonOverlapping.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValuesGeneratorXFixedNonOverlapping.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz / Iain Bryson on 19/07/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartAxisValuesGeneratorXFixedNonOverlapping: ChartAxisValuesGeneratorFixedNonOverlapping { 12 | 13 | public init(axisValues: [ChartAxisValue], spacing: CGFloat = 4) { 14 | super.init(axisValues: axisValues, spacing: spacing, isX: true) 15 | } 16 | } -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisValuesGeneratorYFixedNonOverlapping.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValuesGeneratorYFixedNonOverlapping.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 21/07/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartAxisValuesGeneratorYFixedNonOverlapping: ChartAxisValuesGeneratorFixedNonOverlapping { 12 | 13 | public init(axisValues: [ChartAxisValue], spacing: CGFloat = 4) { 14 | super.init(axisValues: axisValues, spacing: spacing, isX: false) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisXHighLayerDefault.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisXHighLayerDefault.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 25/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// A ChartAxisLayer for high X axes 12 | class ChartAxisXHighLayerDefault: ChartAxisXLayerDefault { 13 | 14 | override var low: Bool {return false} 15 | 16 | /// The start point of the axis line. 17 | override var lineP1: CGPoint { 18 | return CGPoint(x: origin.x, y: origin.y + lineOffset) 19 | } 20 | 21 | /// The end point of the axis line 22 | override var lineP2: CGPoint { 23 | return CGPoint(x: end.x, y: end.y + lineOffset) 24 | } 25 | 26 | /// The offset of the axis labels from the edge of the axis bounds 27 | /// 28 | /// ```` 29 | /// ─ ─ ─ ─ ▲ 30 | /// Title │ 31 | /// │ 32 | /// ▼ 33 | /// Label 34 | /// ```` 35 | fileprivate var labelsOffset: CGFloat { 36 | return axisTitleLabelsHeight + settings.axisTitleLabelsToLabelsSpacing 37 | } 38 | 39 | /// The offset of the axis line from the edge of the axis bounds 40 | /// 41 | /// ```` 42 | /// ─ ─ ─ ─ ▲ 43 | /// Title │ 44 | /// │ 45 | /// │ 46 | /// Label │ 47 | /// │ 48 | /// │ 49 | /// ─────── ▼ 50 | /// ```` 51 | fileprivate var lineOffset: CGFloat { 52 | return labelsOffset + settings.labelsToAxisSpacingX + labelsTotalHeight 53 | } 54 | 55 | override func chartViewDrawing(context: CGContext, chart: Chart) { 56 | super.chartViewDrawing(context: context, chart: chart) 57 | } 58 | 59 | override func updateInternal() { 60 | guard let chart = chart else {return} 61 | 62 | super.updateInternal() 63 | 64 | if lastFrame.height != frame.height, canChangeFrameSize { 65 | chart.notifyAxisInnerFrameChange(xHigh: ChartAxisLayerWithFrameDelta(layer: self, delta: frame.height - lastFrame.height)) 66 | } 67 | } 68 | 69 | override func handleAxisInnerFrameChange(_ xLow: ChartAxisLayerWithFrameDelta?, yLow: ChartAxisLayerWithFrameDelta?, xHigh: ChartAxisLayerWithFrameDelta?, yHigh: ChartAxisLayerWithFrameDelta?) { 70 | super.handleAxisInnerFrameChange(xLow, yLow: yLow, xHigh: xHigh, yHigh: yHigh) 71 | 72 | // handle resizing of other high x axes 73 | if let xHigh = xHigh, xHigh.layer.frame.minY < frame.minY { 74 | offset = offset + xHigh.delta 75 | 76 | initDrawers() 77 | } 78 | } 79 | 80 | override func axisLineY(offset: CGFloat) -> CGFloat { 81 | return lineP1.y + settings.axisStrokeWidth / 2 82 | } 83 | 84 | override func initDrawers() { 85 | axisTitleLabelDrawers = generateAxisTitleLabelsDrawers(offset: 0) 86 | labelDrawers = generateLabelDrawers(offset: labelsOffset) 87 | lineDrawer = generateLineDrawer(offset: lineOffset) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisXLowLayerDefault.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisXLowLayerDefault.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 25/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// A ChartAxisLayer for low X axes 12 | class ChartAxisXLowLayerDefault: ChartAxisXLayerDefault { 13 | 14 | override var low: Bool {return true} 15 | 16 | /// The start point of the axis line. 17 | override var lineP1: CGPoint { 18 | return CGPoint(x: axis.firstVisibleScreen, y: origin.y) 19 | } 20 | 21 | /// The end point of the axis line. 22 | override var lineP2: CGPoint { 23 | return CGPoint(x: axis.lastVisibleScreen, y: end.y) 24 | } 25 | 26 | override func chartViewDrawing(context: CGContext, chart: Chart) { 27 | super.chartViewDrawing(context: context, chart: chart) 28 | } 29 | 30 | override func updateInternal() { 31 | guard let chart = chart else {return} 32 | super.updateInternal() 33 | if lastFrame.height != frame.height { 34 | 35 | // Move drawers by delta 36 | let delta = frame.height - lastFrame.height 37 | offset -= delta 38 | initDrawers() 39 | 40 | if canChangeFrameSize { 41 | chart.notifyAxisInnerFrameChange(xLow: ChartAxisLayerWithFrameDelta(layer: self, delta: delta)) 42 | } 43 | 44 | } 45 | } 46 | 47 | override func handleAxisInnerFrameChange(_ xLow: ChartAxisLayerWithFrameDelta?, yLow: ChartAxisLayerWithFrameDelta?, xHigh: ChartAxisLayerWithFrameDelta?, yHigh: ChartAxisLayerWithFrameDelta?) { 48 | super.handleAxisInnerFrameChange(xLow, yLow: yLow, xHigh: xHigh, yHigh: yHigh) 49 | 50 | // Handle resizing of other low x axes 51 | if let xLow = xLow , xLow.layer.frame.maxY > frame.maxY { 52 | offset = offset - xLow.delta 53 | initDrawers() 54 | } 55 | } 56 | 57 | override func axisLineY(offset: CGFloat) -> CGFloat { 58 | return self.offset + offset + settings.axisStrokeWidth / 2 59 | } 60 | 61 | override func initDrawers() { 62 | lineDrawer = generateLineDrawer(offset: 0) 63 | let labelsOffset = settings.axisStrokeWidth + settings.labelsToAxisSpacingX 64 | labelDrawers = generateLabelDrawers(offset: labelsOffset) 65 | let definitionLabelsOffset = labelsOffset + labelsTotalHeight + settings.axisTitleLabelsToLabelsSpacing 66 | axisTitleLabelDrawers = generateAxisTitleLabelsDrawers(offset: definitionLabelsOffset) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /SwiftCharts/Axis/ChartAxisYHighLayerDefault.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisYHighLayerDefault.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 25/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// A ChartAxisLayer for high Y axes 12 | class ChartAxisYHighLayerDefault: ChartAxisYLayerDefault { 13 | 14 | override var low: Bool {return false} 15 | 16 | /// The start point of the axis line. 17 | override var lineP1: CGPoint { 18 | return CGPoint(x: origin.x, y: axis.firstVisibleScreen) 19 | } 20 | 21 | /// The end point of the axis line. 22 | override var lineP2: CGPoint { 23 | return CGPoint(x: end.x, y: axis.lastVisibleScreen) 24 | } 25 | 26 | override func updateInternal() { 27 | guard let chart = chart else {return} 28 | super.updateInternal() 29 | if lastFrame.width != frame.width { 30 | 31 | // Move drawers by delta 32 | let delta = frame.width - lastFrame.width 33 | offset -= delta 34 | initDrawers() 35 | 36 | if canChangeFrameSize { 37 | chart.notifyAxisInnerFrameChange(yHigh: ChartAxisLayerWithFrameDelta(layer: self, delta: delta)) 38 | } 39 | } 40 | } 41 | 42 | 43 | override func handleAxisInnerFrameChange(_ xLow: ChartAxisLayerWithFrameDelta?, yLow: ChartAxisLayerWithFrameDelta?, xHigh: ChartAxisLayerWithFrameDelta?, yHigh: ChartAxisLayerWithFrameDelta?) { 44 | super.handleAxisInnerFrameChange(xLow, yLow: yLow, xHigh: xHigh, yHigh: yHigh) 45 | 46 | // Handle resizing of other high y axes 47 | if let yHigh = yHigh , yHigh.layer.frame.maxX > frame.maxX { 48 | offset = offset - yHigh.delta 49 | initDrawers() 50 | } 51 | } 52 | 53 | override func initDrawers() { 54 | 55 | lineDrawer = generateLineDrawer(offset: 0) 56 | 57 | let labelsOffset = settings.labelsToAxisSpacingY + settings.axisStrokeWidth 58 | labelDrawers = generateLabelDrawers(offset: labelsOffset) 59 | let axisTitleLabelsOffset = labelsOffset + labelsMaxWidth + settings.axisTitleLabelsToLabelsSpacing 60 | axisTitleLabelDrawers = generateAxisTitleLabelsDrawers(offset: axisTitleLabelsOffset) 61 | } 62 | 63 | override func generateLineDrawer(offset: CGFloat) -> ChartLineDrawer { 64 | let halfStrokeWidth = settings.axisStrokeWidth / 2 // we want that the stroke begins at the beginning of the frame, not in the middle of it 65 | let x = origin.x + offset + halfStrokeWidth 66 | let p1 = CGPoint(x: x, y: axis.firstVisibleScreen) 67 | let p2 = CGPoint(x: x, y: axis.lastVisibleScreen) 68 | return ChartLineDrawer(p1: p1, p2: p2, color: settings.lineColor, strokeWidth: settings.axisStrokeWidth) 69 | } 70 | 71 | override func labelsX(offset: CGFloat, labelWidth: CGFloat, textAlignment: ChartLabelTextAlignment) -> CGFloat { 72 | var labelsX: CGFloat 73 | switch textAlignment { 74 | case .left, .default: 75 | labelsX = origin.x + offset 76 | case .right: 77 | labelsX = origin.x + offset + labelsMaxWidth - labelWidth 78 | } 79 | return labelsX 80 | } 81 | 82 | override func axisLineX(offset: CGFloat) -> CGFloat { 83 | return self.offset + offset + settings.axisStrokeWidth / 2 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /SwiftCharts/AxisValues/ChartAxisValue.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValue.swift 3 | // swift_charts 4 | // 5 | // Created by ischuetz on 01/03/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /** 12 | A ChartAxisValue models a value along a particular chart axis. For example, two ChartAxisValues represent the two components of a ChartPoint. It has a backing Double scalar value, which provides a canonical form for all subclasses to be laid out along an axis. It also has one or more labels that are drawn in the chart. 13 | This class is not meant to be instantiated directly. Use one of the existing subclasses or create a new one. 14 | */ 15 | open class ChartAxisValue: Equatable, Hashable, CustomStringConvertible { 16 | 17 | /// The backing value for all other types of axis values 18 | public let scalar: Double 19 | public let labelSettings: ChartLabelSettings 20 | public var hidden = false 21 | 22 | /// The labels that will be displayed in the chart 23 | open var labels: [ChartAxisLabel] { 24 | let axisLabel = ChartAxisLabel(text: description, settings: labelSettings) 25 | axisLabel.hidden = hidden 26 | return [axisLabel] 27 | } 28 | 29 | public init(scalar: Double, labelSettings: ChartLabelSettings = ChartLabelSettings()) { 30 | self.scalar = scalar 31 | self.labelSettings = labelSettings 32 | } 33 | 34 | open var copy: ChartAxisValue { 35 | return copy(scalar) 36 | } 37 | 38 | open func copy(_ scalar: Double) -> ChartAxisValue { 39 | return ChartAxisValue(scalar: scalar, labelSettings: labelSettings) 40 | } 41 | 42 | // MARK: CustomStringConvertible 43 | 44 | open var description: String { 45 | return String(scalar) 46 | } 47 | 48 | open func hash(into hasher: inout Hasher) { 49 | hasher.combine(self.scalar.hashValue) 50 | } 51 | } 52 | 53 | public func ==(lhs: ChartAxisValue, rhs: ChartAxisValue) -> Bool { 54 | return lhs.scalar =~ rhs.scalar 55 | } 56 | -------------------------------------------------------------------------------- /SwiftCharts/AxisValues/ChartAxisValueDate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValueDate.swift 3 | // swift_charts 4 | // 5 | // Created by ischuetz on 01/03/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartAxisValueDate: ChartAxisValue { 12 | 13 | fileprivate let formatter: (Date) -> String 14 | 15 | open var date: Date { 16 | return ChartAxisValueDate.dateFromScalar(scalar) 17 | } 18 | 19 | public init(date: Date, formatter: @escaping (Date) -> String, labelSettings: ChartLabelSettings = ChartLabelSettings()) { 20 | self.formatter = formatter 21 | super.init(scalar: ChartAxisValueDate.scalarFromDate(date), labelSettings: labelSettings) 22 | } 23 | 24 | convenience public init(date: Date, formatter: DateFormatter, labelSettings: ChartLabelSettings = ChartLabelSettings()) { 25 | self.init(date: date, formatter: { formatter.string(from: $0) }, labelSettings: labelSettings) 26 | } 27 | 28 | open class func dateFromScalar(_ scalar: Double) -> Date { 29 | return Date(timeIntervalSince1970: TimeInterval(scalar)) 30 | } 31 | 32 | open class func scalarFromDate(_ date: Date) -> Double { 33 | return Double(date.timeIntervalSince1970) 34 | } 35 | 36 | // MARK: CustomStringConvertible 37 | 38 | override open var description: String { 39 | return formatter(date) 40 | } 41 | 42 | open override func copy(_ scalar: Double) -> ChartAxisValue { 43 | return ChartAxisValueDate(date: ChartAxisValueDate.dateFromScalar(scalar), formatter: formatter, labelSettings: labelSettings) 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /SwiftCharts/AxisValues/ChartAxisValueDouble.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValueDouble.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 30/08/15. 6 | // Copyright © 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartAxisValueDouble: ChartAxisValue { 12 | 13 | public let formatter: NumberFormatter 14 | 15 | public convenience init(_ int: Int, formatter: NumberFormatter = ChartAxisValueDouble.defaultFormatter, labelSettings: ChartLabelSettings = ChartLabelSettings()) { 16 | self.init(Double(int), formatter: formatter, labelSettings: labelSettings) 17 | } 18 | 19 | public init(_ double: Double, formatter: NumberFormatter = ChartAxisValueDouble.defaultFormatter, labelSettings: ChartLabelSettings = ChartLabelSettings()) { 20 | self.formatter = formatter 21 | super.init(scalar: double, labelSettings: labelSettings) 22 | } 23 | 24 | override open func copy(_ scalar: Double) -> ChartAxisValueDouble { 25 | return ChartAxisValueDouble(scalar, formatter: formatter, labelSettings: labelSettings) 26 | } 27 | 28 | public static var defaultFormatter: NumberFormatter = { 29 | let formatter = NumberFormatter() 30 | formatter.maximumFractionDigits = 2 31 | return formatter 32 | }() 33 | 34 | // MARK: CustomStringConvertible 35 | 36 | override open var description: String { 37 | return formatter.string(from: NSNumber(value: scalar))! 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /SwiftCharts/AxisValues/ChartAxisValueDoubleScreenLoc.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValueDoubleScreenLoc.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 30/08/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartAxisValueDoubleScreenLoc: ChartAxisValueDouble { 12 | 13 | fileprivate let actualDouble: Double 14 | 15 | var screenLocDouble: Double { 16 | return scalar 17 | } 18 | 19 | // screenLocFloat: model value which will be used to calculate screen position 20 | // actualFloat: scalar which this axis value really represents 21 | public init(screenLocDouble: Double, actualDouble: Double, formatter: NumberFormatter = ChartAxisValueDouble.defaultFormatter, labelSettings: ChartLabelSettings = ChartLabelSettings()) { 22 | self.actualDouble = actualDouble 23 | super.init(screenLocDouble, formatter: formatter, labelSettings: labelSettings) 24 | } 25 | 26 | // MARK: CustomStringConvertible 27 | 28 | override open var description: String { 29 | return formatter.string(from: NSNumber(value: actualDouble))! 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /SwiftCharts/AxisValues/ChartAxisValueFloat.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValueFloat.swift 3 | // swift_charts 4 | // 5 | // Created by ischuetz on 15/03/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @available(*, deprecated, message: "use ChartAxisValueDouble instead") 12 | open class ChartAxisValueFloat: ChartAxisValue { 13 | 14 | public let formatter: NumberFormatter 15 | 16 | open var float: CGFloat { 17 | return CGFloat(scalar) 18 | } 19 | 20 | public init(_ float: CGFloat, formatter: NumberFormatter = ChartAxisValueFloat.defaultFormatter, labelSettings: ChartLabelSettings = ChartLabelSettings()) { 21 | self.formatter = formatter 22 | super.init(scalar: Double(float), labelSettings: labelSettings) 23 | } 24 | 25 | override open func copy(_ scalar: Double) -> ChartAxisValueFloat { 26 | return ChartAxisValueFloat(CGFloat(scalar), formatter: formatter, labelSettings: labelSettings) 27 | } 28 | 29 | public static var defaultFormatter: NumberFormatter = { 30 | let formatter = NumberFormatter() 31 | formatter.maximumFractionDigits = 2 32 | return formatter 33 | }() 34 | 35 | // MARK: CustomStringConvertible 36 | 37 | override open var description: String { 38 | return formatter.string(from: NSNumber(value: Float(float)))! 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /SwiftCharts/AxisValues/ChartAxisValueFloatScreenLoc.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValueFloatScreenLoc.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 30/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @available(*, deprecated, message: "use ChartAxisValueDoubleScreenLoc instead") 12 | open class ChartAxisValueFloatScreenLoc: ChartAxisValueFloat { 13 | 14 | fileprivate let actualFloat: CGFloat 15 | 16 | var screenLocFloat: CGFloat { 17 | return CGFloat(scalar) 18 | } 19 | 20 | // screenLocFloat: model value which will be used to calculate screen position 21 | // actualFloat: scalar which this axis value really represents 22 | public init(screenLocFloat: CGFloat, actualFloat: CGFloat, formatter: NumberFormatter = ChartAxisValueFloat.defaultFormatter, labelSettings: ChartLabelSettings = ChartLabelSettings()) { 23 | self.actualFloat = actualFloat 24 | super.init(screenLocFloat, formatter: formatter, labelSettings: labelSettings) 25 | } 26 | 27 | // MARK: CustomStringConvertible 28 | 29 | override open var description: String { 30 | return self.formatter.string(from: NSNumber(value: Float(actualFloat)))! 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SwiftCharts/AxisValues/ChartAxisValueInt.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValueInt.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 04/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartAxisValueInt: ChartAxisValue { 12 | 13 | public let int: Int 14 | 15 | public init(_ int: Int, labelSettings: ChartLabelSettings = ChartLabelSettings()) { 16 | self.int = int 17 | super.init(scalar: Double(int), labelSettings: labelSettings) 18 | } 19 | 20 | override open func copy(_ scalar: Double) -> ChartAxisValueInt { 21 | return ChartAxisValueInt(int, labelSettings: labelSettings) 22 | } 23 | 24 | // MARK: CustomStringConvertible 25 | 26 | override open var description: String { 27 | return String(int) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SwiftCharts/AxisValues/ChartAxisValueString.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValueString.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 29/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartAxisValueString: ChartAxisValue { 12 | 13 | public let string: String 14 | 15 | public init(_ string: String = "", order: Int, labelSettings: ChartLabelSettings = ChartLabelSettings()) { 16 | self.string = string 17 | super.init(scalar: Double(order), labelSettings: labelSettings) 18 | } 19 | 20 | // MARK: CustomStringConvertible 21 | 22 | override open var description: String { 23 | return string 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SwiftCharts/CGPoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPoint.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 30/07/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension CGPoint { 12 | 13 | func distance(_ point: CGPoint) -> CGFloat { 14 | return CGFloat(hypotf(Float(x) - Float(point.x), Float(y) - Float(point.y))) 15 | } 16 | 17 | func add(_ point: CGPoint) -> CGPoint { 18 | return offset(x: point.x, y: point.y) 19 | } 20 | 21 | func substract(_ point: CGPoint) -> CGPoint { 22 | return offset(x: -point.x, y: -point.y) 23 | } 24 | 25 | func offset(x: CGFloat = 0, y: CGFloat = 0) -> CGPoint { 26 | return CGPoint(x: self.x + x, y: self.y + y) 27 | } 28 | 29 | func surroundingRect(_ size: CGFloat) -> CGRect { 30 | return CGRect(x: x - size / 2, y: y - size / 2, width: size, height: size) 31 | } 32 | 33 | func nearest(_ intersections: [CGPoint]) -> (distance: CGFloat, point: CGPoint)? { 34 | return nearest(intersections, pointMapper: {$0}).map{(distance: $0.distance, point: $0.pointMappable)} 35 | } 36 | 37 | /// Finds nearest object which can be mapped to a point using pointMapper function. This is convenient for objects that contain/represent a point, in order to avoid having to map to points and back. 38 | func nearest(_ pointMappables: [T], pointMapper: (T) -> CGPoint) -> (distance: CGFloat, pointMappable: T)? { 39 | var minDistancePoint: (distance: CGFloat, pointMappable: T)? = nil 40 | for pointMappable in pointMappables { 41 | let dist = distance(pointMapper(pointMappable)) 42 | if (minDistancePoint.map{dist < $0.0}) ?? true { 43 | minDistancePoint = (dist, pointMappable) 44 | } 45 | } 46 | return minDistancePoint 47 | } 48 | 49 | func multiplyBy(_ value:CGFloat) -> CGPoint{ 50 | return CGPoint(x: x * value, y: y * value) 51 | } 52 | 53 | func length() -> CGFloat { 54 | return CGFloat(sqrt(CDouble( 55 | x * x + y * y 56 | ))) 57 | } 58 | 59 | func normalize() -> CGPoint { 60 | let l = length() 61 | return CGPoint(x: x / l, y: y / l) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /SwiftCharts/CGRect.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGRect.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 13/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension CGRect { 12 | 13 | public func insetBy(dx: CGFloat = 0, dy: CGFloat = 0, dw: CGFloat = 0, dh: CGFloat = 0) -> CGRect { 14 | return CGRect( 15 | x: origin.x + dx, 16 | y: origin.y + dy, 17 | width: width - dw - dx, 18 | height: height - dh - dy 19 | ) 20 | } 21 | 22 | func asLinesArray() -> [(p1: CGPoint, p2: CGPoint)] { 23 | return [ 24 | (p1: CGPoint(x: minX, y: minY), p2: CGPoint(x: maxX, y: minY)), 25 | (p1: CGPoint(x: maxX, y: minY), p2: CGPoint(x: maxX, y: maxY)), 26 | (p1: CGPoint(x: maxX, y: maxY), p2: CGPoint(x: minX, y: maxY)), 27 | (p1: CGPoint(x: minX, y: maxY), p2: CGPoint(x: minX, y: minY)) 28 | ] 29 | } 30 | var center: CGPoint { 31 | return CGPoint(x: width / 2, y: height / 2) 32 | } 33 | 34 | 35 | /** 36 | Calculates the bounding rectangle of a rectangle after it's rotated. 37 | 38 | Source: http://stackoverflow.com/a/9168238/930450 39 | 40 | - parameter radians: The angle in radians that it's to be rotated 41 | 42 | - returns: The bounding rectangle of the rotated rectangle 43 | */ 44 | public func boundingRectAfterRotating(radians: CGFloat) -> CGRect { 45 | return applying(CGAffineTransform(rotationAngle: radians)) 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /SwiftCharts/ChartAxisValueArray.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartAxisValue.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 26/07/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension Array where Element: ChartAxisValue { 12 | 13 | func calculateLabelsDimensions() -> (total: CGSize, max: CGSize) { 14 | return compactMap({ 15 | guard let label = $0.labels.first else {return nil} 16 | return label.textSizeNonRotated 17 | }).reduce((total: CGSize.zero, max: CGSize.zero), {(lhs: (total: CGSize, max: CGSize), rhs: CGSize) in 18 | return ( 19 | CGSize(width: lhs.total.width + rhs.width, height: lhs.total.height + rhs.height), 20 | CGSize(width: Swift.max(lhs.max.width, rhs.width), height: Swift.max(lhs.max.height, rhs.height)) 21 | ) 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SwiftCharts/ChartLineModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartLineModel.swift 3 | // swift_charts 4 | // 5 | // Created by ischuetz on 11/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Models a line to be drawn in a chart based on an array of chart points. 12 | public struct ChartLineModel { 13 | 14 | /// The array of chart points that the line should be drawn with. In a simple case this would be drawn as straight line segments connecting each point. 15 | public let chartPoints: [T] 16 | 17 | /// The color(s) that the line is drawn with 18 | public let lineColors: [UIColor] 19 | 20 | /// The width of the line in points 21 | public let lineWidth: CGFloat 22 | 23 | public let lineJoin: LineJoin 24 | 25 | public let lineCap: LineCap 26 | 27 | /// The duration in seconds of the animation that is run when the line appears 28 | public let animDuration: Float 29 | 30 | /// The delay in seconds before the animation runs 31 | public let animDelay: Float 32 | 33 | /// The dash pattern for the line 34 | public let dashPattern: [Double]? 35 | 36 | public init(chartPoints: [T], lineColors: [UIColor], lineWidth: CGFloat = 1, lineJoin: LineJoin = .round, lineCap: LineCap = .round, animDuration: Float, animDelay: Float, dashPattern: [Double]? = nil) { 37 | self.chartPoints = chartPoints 38 | self.lineColors = lineColors 39 | self.lineWidth = lineWidth 40 | self.lineJoin = lineJoin 41 | self.lineCap = lineCap 42 | self.animDuration = animDuration 43 | self.animDelay = animDelay 44 | self.dashPattern = dashPattern 45 | } 46 | 47 | public init(chartPoints: [T], lineColor: UIColor, lineWidth: CGFloat = 1, lineJoin: LineJoin = .round, lineCap: LineCap = .round, animDuration: Float, animDelay: Float, dashPattern: [Double]? = nil) { 48 | self.init(chartPoints: chartPoints, lineColors: [lineColor], lineWidth: lineWidth, lineJoin: lineJoin, lineCap: lineCap, animDuration: animDuration, animDelay: animDelay, dashPattern: dashPattern) 49 | } 50 | /// The number of chart points in the model 51 | var chartPointsCount: Int { 52 | return chartPoints.count 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /SwiftCharts/ChartPoint/ChartPoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPoint.swift 3 | // swift_charts 4 | // 5 | // Created by ischuetz on 01/03/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartPoint: Hashable, Equatable, CustomStringConvertible { 12 | 13 | public let x: ChartAxisValue 14 | public let y: ChartAxisValue 15 | 16 | required public init(x: ChartAxisValue, y: ChartAxisValue) { 17 | self.x = x 18 | self.y = y 19 | } 20 | 21 | open var description: String { 22 | return "\(x), \(y)" 23 | } 24 | 25 | open func hash(into hasher: inout Hasher) { 26 | let hash = 31 &* x.hashValue &+ y.hashValue 27 | hasher.combine(hash) 28 | } 29 | } 30 | 31 | public func ==(lhs: ChartPoint, rhs: ChartPoint) -> Bool { 32 | return lhs.x == rhs.x && lhs.y == rhs.y 33 | } 34 | -------------------------------------------------------------------------------- /SwiftCharts/ChartPoint/ChartPointBubble.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPointBubble.swift 3 | // Examples 4 | // 5 | // Created by ischuetz on 17/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartPointBubble: ChartPoint { 12 | public let diameterScalar: Double 13 | public let bgColor: UIColor 14 | public let borderColor: UIColor 15 | 16 | public init(x: ChartAxisValue, y: ChartAxisValue, diameterScalar: Double, bgColor: UIColor, borderColor: UIColor = UIColor.black) { 17 | self.diameterScalar = diameterScalar 18 | self.bgColor = bgColor 19 | self.borderColor = borderColor 20 | super.init(x: x, y: y) 21 | } 22 | 23 | required public init(x: ChartAxisValue, y: ChartAxisValue) { 24 | fatalError("init(x:y:) has not been implemented") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SwiftCharts/ChartPoint/ChartPointCandleStick.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPointCandleStick.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 28/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartPointCandleStick: ChartPoint { 12 | 13 | public let date: Date 14 | public let open: Double 15 | public let close: Double 16 | public let low: Double 17 | public let high: Double 18 | 19 | public init(date: Date, formatter: DateFormatter, high: Double, low: Double, open: Double, close: Double, labelHidden: Bool = false) { 20 | 21 | let x = ChartAxisValueDate(date: date, formatter: formatter) 22 | self.date = date 23 | x.hidden = labelHidden 24 | 25 | let highY = ChartAxisValueDouble(high) 26 | self.high = high 27 | self.low = low 28 | self.open = open 29 | self.close = close 30 | 31 | super.init(x: x, y: highY) 32 | } 33 | 34 | required public init(x: ChartAxisValue, y: ChartAxisValue) { 35 | fatalError("init(x:y:) has not been implemented") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SwiftCharts/ChartViewsConflictSolver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartViewsConflictSolver.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 30/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// For now as class, which in this case is acceptable. Protocols currently don't work very well with generics. 12 | open class ChartViewsConflictSolver { 13 | 14 | /** 15 | Repositions views if they overlap 16 | 17 | - parameter views: The views to check for overlap and resolve 18 | */ 19 | func solveConflicts(views: [ChartPointsViewsLayer.ViewWithChartPoint]) {} 20 | } 21 | -------------------------------------------------------------------------------- /SwiftCharts/Convenience/BarsChart.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BarsChart.swift 3 | // Examples 4 | // 5 | // Created by ischuetz on 19/07/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class BarsChartConfig: ChartConfig { 12 | public let valsAxisConfig: ChartAxisConfig 13 | public let xAxisLabelSettings: ChartLabelSettings 14 | public let yAxisLabelSettings: ChartLabelSettings 15 | 16 | public init(chartSettings: ChartSettings = ChartSettings(), valsAxisConfig: ChartAxisConfig, xAxisLabelSettings: ChartLabelSettings = ChartLabelSettings(), yAxisLabelSettings: ChartLabelSettings = ChartLabelSettings(), guidelinesConfig: GuidelinesConfig = GuidelinesConfig()) { 17 | self.valsAxisConfig = valsAxisConfig 18 | self.xAxisLabelSettings = xAxisLabelSettings 19 | self.yAxisLabelSettings = yAxisLabelSettings 20 | 21 | super.init(chartSettings: chartSettings, guidelinesConfig: guidelinesConfig) 22 | } 23 | } 24 | 25 | open class BarsChart: Chart { 26 | 27 | public init(frame: CGRect, chartConfig: BarsChartConfig, xTitle: String, yTitle: String, bars barModels: [(String, Double)], color: UIColor, barWidth: CGFloat, animDuration: Float = 0.5, animDelay: Float = 0.5, horizontal: Bool = false) { 28 | 29 | let zero = ChartAxisValueDouble(0) 30 | let bars: [ChartBarModel] = barModels.enumerated().map { index, barModel in 31 | return ChartBarModel(constant: ChartAxisValueDouble(index), axisValue1: zero, axisValue2: ChartAxisValueDouble(barModel.1), bgColor: color) 32 | } 33 | 34 | let valAxisValues = stride(from: chartConfig.valsAxisConfig.from, through: chartConfig.valsAxisConfig.to, by: chartConfig.valsAxisConfig.by).map{ChartAxisValueDouble($0, labelSettings : chartConfig.xAxisLabelSettings)} 35 | let labelAxisValues = [ChartAxisValueString(order: -1)] + barModels.enumerated().map{ index, tuple in 36 | ChartAxisValueString(tuple.0, order: index, labelSettings : chartConfig.xAxisLabelSettings) 37 | } + [ChartAxisValueString(order: barModels.count)] 38 | 39 | let (xValues, yValues): ([ChartAxisValue], [ChartAxisValue]) = horizontal ? (valAxisValues, labelAxisValues) : (labelAxisValues, valAxisValues) 40 | 41 | let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: xTitle, settings: chartConfig.xAxisLabelSettings)) 42 | let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: yTitle, settings: chartConfig.xAxisLabelSettings.defaultVertical())) 43 | let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartConfig.chartSettings, chartFrame: frame, xModel: xModel, yModel: yModel) 44 | let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame) 45 | 46 | let barViewSettings = ChartBarViewSettings(animDuration: animDuration, animDelay: animDelay) 47 | 48 | let barsLayer: ChartLayer = ChartBarsLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, bars: bars, horizontal: horizontal, barWidth: barWidth, settings: barViewSettings) 49 | 50 | let guidelinesLayer = GuidelinesDefaultLayerGenerator.generateOpt(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, guidelinesConfig: chartConfig.guidelinesConfig) 51 | 52 | let view = ChartBaseView(frame: frame) 53 | let layers: [ChartLayer] = [xAxisLayer, yAxisLayer] + (guidelinesLayer.map{[$0]} ?? []) + [barsLayer] 54 | 55 | super.init( 56 | view: view, 57 | innerFrame: innerFrame, 58 | settings: chartConfig.chartSettings, 59 | layers: layers 60 | ) 61 | } 62 | 63 | public required init(coder aDecoder: NSCoder) { 64 | fatalError("init(coder:) has not been implemented") 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /SwiftCharts/Convenience/ChartConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartConfig.swift 3 | // Examples 4 | // 5 | // Settings wrappers for default charts. 6 | // These charts are assumed to have one x axis at the bottom and one y axis at the left. 7 | // 8 | // Created by ischuetz on 19/07/15. 9 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 10 | // 11 | 12 | import UIKit 13 | 14 | open class ChartConfig { 15 | public let chartSettings: ChartSettings 16 | public let guidelinesConfig: GuidelinesConfig? // nil means no guidelines 17 | 18 | public init(chartSettings: ChartSettings, guidelinesConfig: GuidelinesConfig?) { 19 | self.chartSettings = chartSettings 20 | self.guidelinesConfig = guidelinesConfig 21 | } 22 | } 23 | 24 | 25 | open class ChartConfigXY: ChartConfig { 26 | public let xAxisConfig: ChartAxisConfig 27 | public let yAxisConfig: ChartAxisConfig 28 | public let xAxisLabelSettings: ChartLabelSettings 29 | public let yAxisLabelSettings: ChartLabelSettings 30 | 31 | public init(chartSettings: ChartSettings = ChartSettings(), xAxisConfig: ChartAxisConfig, yAxisConfig: ChartAxisConfig, xAxisLabelSettings: ChartLabelSettings = ChartLabelSettings(), yAxisLabelSettings: ChartLabelSettings = ChartLabelSettings(), guidelinesConfig: GuidelinesConfig? = GuidelinesConfig()) { 32 | self.xAxisConfig = xAxisConfig 33 | self.yAxisConfig = yAxisConfig 34 | self.xAxisLabelSettings = xAxisLabelSettings 35 | self.yAxisLabelSettings = yAxisLabelSettings 36 | 37 | super.init(chartSettings: chartSettings, guidelinesConfig: guidelinesConfig) 38 | } 39 | } 40 | 41 | public struct ChartAxisConfig { 42 | public let from: Double 43 | public let to: Double 44 | public let by: Double 45 | 46 | public init(from: Double, to: Double, by: Double) { 47 | self.from = from 48 | self.to = to 49 | self.by = by 50 | } 51 | } 52 | 53 | public struct GuidelinesConfig { 54 | public let dotted: Bool 55 | public let lineWidth: CGFloat 56 | public let lineColor: UIColor 57 | 58 | public init(dotted: Bool = true, lineWidth: CGFloat = 0.1, lineColor: UIColor = UIColor.black) { 59 | self.dotted = dotted 60 | self.lineWidth = lineWidth 61 | self.lineColor = lineColor 62 | } 63 | } 64 | 65 | // Helper to generate default guidelines layer for GuidelinesConfig 66 | public struct GuidelinesDefaultLayerGenerator { 67 | 68 | public static func generateOpt(xAxisLayer: ChartAxisLayer, yAxisLayer: ChartAxisLayer, guidelinesConfig: GuidelinesConfig?) -> ChartLayer? { 69 | if let guidelinesConfig = guidelinesConfig { 70 | return generate(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, guidelinesConfig: guidelinesConfig) 71 | } else { 72 | return nil 73 | } 74 | } 75 | 76 | public static func generate(xAxisLayer: ChartAxisLayer, yAxisLayer: ChartAxisLayer, guidelinesConfig: GuidelinesConfig) -> ChartLayer { 77 | if guidelinesConfig.dotted { 78 | let settings = ChartGuideLinesDottedLayerSettings(linesColor: guidelinesConfig.lineColor, linesWidth: guidelinesConfig.lineWidth) 79 | return ChartGuideLinesDottedLayer(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, settings: settings) 80 | 81 | } else { 82 | let settings = ChartGuideLinesDottedLayerSettings(linesColor: guidelinesConfig.lineColor, linesWidth: guidelinesConfig.lineWidth) 83 | return ChartGuideLinesDottedLayer(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, settings: settings) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /SwiftCharts/Convenience/LineChart.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LineChart.swift 3 | // Examples 4 | // 5 | // Created by ischuetz on 19/07/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class LineChart: Chart { 12 | 13 | public typealias ChartLine = (chartPoints: [(Double, Double)], color: UIColor) 14 | 15 | // Initializer for single line 16 | public convenience init(frame: CGRect, chartConfig: ChartConfigXY, xTitle: String, yTitle: String, line: ChartLine) { 17 | self.init(frame: frame, chartConfig: chartConfig, xTitle: xTitle, yTitle: yTitle, lines: [line]) 18 | } 19 | 20 | // Initializer for multiple lines 21 | public init(frame: CGRect, chartConfig: ChartConfigXY, xTitle: String, yTitle: String, lines: [ChartLine]) { 22 | 23 | let xValues = stride(from: chartConfig.xAxisConfig.from, through: chartConfig.xAxisConfig.to, by: chartConfig.xAxisConfig.by).map{ChartAxisValueDouble($0)} 24 | let yValues = stride(from: chartConfig.yAxisConfig.from, through: chartConfig.yAxisConfig.to, by: chartConfig.yAxisConfig.by).map{ChartAxisValueDouble($0)} 25 | 26 | let xModel = ChartAxisModel(axisValues: xValues, axisTitleLabel: ChartAxisLabel(text: xTitle, settings: chartConfig.xAxisLabelSettings)) 27 | let yModel = ChartAxisModel(axisValues: yValues, axisTitleLabel: ChartAxisLabel(text: yTitle, settings: chartConfig.yAxisLabelSettings.defaultVertical())) 28 | let coordsSpace = ChartCoordsSpaceLeftBottomSingleAxis(chartSettings: chartConfig.chartSettings, chartFrame: frame, xModel: xModel, yModel: yModel) 29 | let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame) 30 | 31 | let lineLayers: [ChartLayer] = lines.map {line in 32 | let chartPoints = line.chartPoints.map {chartPointScalar in 33 | ChartPoint(x: ChartAxisValueDouble(chartPointScalar.0), y: ChartAxisValueDouble(chartPointScalar.1)) 34 | } 35 | 36 | let lineModel = ChartLineModel(chartPoints: chartPoints, lineColor: line.color, animDuration: 0.5, animDelay: 0) 37 | return ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel]) 38 | } 39 | 40 | let guidelinesLayer = GuidelinesDefaultLayerGenerator.generateOpt(xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, guidelinesConfig: chartConfig.guidelinesConfig) 41 | 42 | let view = ChartBaseView(frame: frame) 43 | let layers: [ChartLayer] = [xAxisLayer, yAxisLayer] + (guidelinesLayer.map{[$0]} ?? []) + lineLayers 44 | 45 | super.init( 46 | view: view, 47 | innerFrame: innerFrame, 48 | settings: chartConfig.chartSettings, 49 | layers: layers 50 | ) 51 | } 52 | 53 | public required init(coder aDecoder: NSCoder) { 54 | fatalError("init(coder:) has not been implemented") 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /SwiftCharts/Drawers/ChartContextDrawer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartContextDrawer.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 25/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartContextDrawer { 12 | 13 | var hidden: Bool = false 14 | 15 | final func triggerDraw(context: CGContext, chart: Chart) { 16 | if !hidden { 17 | self.draw(context: context, chart: chart) 18 | } 19 | } 20 | 21 | func draw(context: CGContext, chart: Chart) {} 22 | } 23 | -------------------------------------------------------------------------------- /SwiftCharts/Drawers/ChartDrawerFunctions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartDrawerFunctions.swift 3 | // Examples 4 | // 5 | // Created by ischuetz on 21/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | func ChartDrawLine(context: CGContext, p1: CGPoint, p2: CGPoint, width: CGFloat, color: UIColor) { 12 | context.setStrokeColor(color.cgColor) 13 | context.setLineWidth(width) 14 | context.move(to: CGPoint(x: p1.x, y: p1.y)) 15 | context.addLine(to: CGPoint(x: p2.x, y: p2.y)) 16 | context.strokePath() 17 | } 18 | 19 | func ChartDrawDottedLine(context: CGContext, p1: CGPoint, p2: CGPoint, width: CGFloat, color: UIColor, dotWidth: CGFloat, dotSpacing: CGFloat) { 20 | context.setStrokeColor(color.cgColor) 21 | context.setLineWidth(width) 22 | context.setLineDash(phase: 0, lengths: [dotWidth, dotSpacing]) 23 | context.move(to: CGPoint(x: p1.x, y: p1.y)) 24 | context.addLine(to: CGPoint(x: p2.x, y: p2.y)) 25 | context.strokePath() 26 | context.setLineDash(phase: 0, lengths: []) 27 | } 28 | -------------------------------------------------------------------------------- /SwiftCharts/Drawers/ChartLineDrawer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartLineDrawer.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 25/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ChartLineDrawer: ChartContextDrawer { 12 | fileprivate let p1: CGPoint 13 | fileprivate let p2: CGPoint 14 | fileprivate let color: UIColor 15 | fileprivate let strokeWidth: CGFloat 16 | 17 | init(p1: CGPoint, p2: CGPoint, color: UIColor, strokeWidth: CGFloat = 0.2) { 18 | self.p1 = p1 19 | self.p2 = p2 20 | self.color = color 21 | self.strokeWidth = strokeWidth 22 | } 23 | 24 | override func draw(context: CGContext, chart: Chart) { 25 | ChartDrawLine(context: context, p1: p1, p2: p2, width: strokeWidth, color: color) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SwiftCharts/Globals.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Globals.swift 3 | // SwiftCharts 4 | // 5 | // Created by Ivan Schütz on 27/10/2016. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | func _max(a: T, b: T) -> T { 12 | return max(a, b) 13 | } 14 | -------------------------------------------------------------------------------- /SwiftCharts/Int.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Int.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 26/07/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension Int { 12 | 13 | // Lazily computes prime factors 14 | // src: https://gist.github.com/JadenGeller/f998071fe71c2bc2976ef6465f0e6e5d 15 | public var primeFactors: AnySequence { 16 | func factor(_ input: Int) -> (prime: Int, remainder: Int) { 17 | let end = Int(sqrt(Float(input))) 18 | if end > 2 { 19 | for prime in 2...end { 20 | if input % prime == 0 { 21 | return (prime, input / prime) 22 | } 23 | } 24 | } 25 | return (input, 1) 26 | } 27 | var current = self 28 | return AnySequence(AnyIterator { 29 | guard current != 1 else { return nil } 30 | 31 | let result = factor(current) 32 | current = result.remainder 33 | return result.prime 34 | }) 35 | } 36 | 37 | func floorMultiple(_ value: Int) -> Int { 38 | return Int(floor(Double(self) / Double(value)) * Double(value)) 39 | } 40 | 41 | func ceilMultiple(_ value: Int) -> Int { 42 | return Int(ceil(Double(self) / Double(value)) * Double(value)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SwiftCharts/Layers/ChartDividersLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartDividersLayer.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 21/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct ChartDividersLayerSettings { 12 | let linesColor: UIColor 13 | let linesWidth: CGFloat 14 | let start: CGFloat // Points from start to axis, axis is 0 15 | let end: CGFloat // Points from axis to end, axis is 0 16 | let show: ((Double) -> Bool)? // Set visibility of individual axis values. If nil, all axis values will be shown. 17 | 18 | public init(linesColor: UIColor = UIColor.gray, linesWidth: CGFloat = 0.3, start: CGFloat = 5, end: CGFloat = 5, show: ((Double) -> Bool)? = nil) { 19 | self.linesColor = linesColor 20 | self.linesWidth = linesWidth 21 | self.start = start 22 | self.end = end 23 | self.show = show 24 | } 25 | } 26 | 27 | public enum ChartDividersLayerAxis { 28 | case x, y, xAndY 29 | } 30 | 31 | open class ChartDividersLayer: ChartCoordsSpaceLayer { 32 | 33 | fileprivate let settings: ChartDividersLayerSettings 34 | 35 | let axis: ChartDividersLayerAxis 36 | 37 | fileprivate let xAxisLayer: ChartAxisLayer 38 | fileprivate let yAxisLayer: ChartAxisLayer 39 | 40 | public init(xAxisLayer: ChartAxisLayer, yAxisLayer: ChartAxisLayer, axis: ChartDividersLayerAxis = .xAndY, settings: ChartDividersLayerSettings) { 41 | self.axis = axis 42 | self.settings = settings 43 | 44 | self.xAxisLayer = xAxisLayer 45 | self.yAxisLayer = yAxisLayer 46 | 47 | super.init(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis) 48 | } 49 | 50 | fileprivate func drawLine(context: CGContext, color: UIColor, width: CGFloat, p1: CGPoint, p2: CGPoint) { 51 | ChartDrawLine(context: context, p1: p1, p2: p2, width: width, color: color) 52 | } 53 | 54 | override open func chartViewDrawing(context: CGContext, chart: Chart) { 55 | let xValues = xAxisLayer.currentAxisValues 56 | let yValues = yAxisLayer.currentAxisValues 57 | 58 | if axis == .x || axis == .xAndY { 59 | for xValue in xValues { 60 | guard (settings.show?(xValue) ?? true) else {continue} 61 | 62 | let xScreenLoc = xAxisLayer.axis.screenLocForScalar(xValue) 63 | 64 | let x1 = xScreenLoc 65 | let y1 = xAxisLayer.lineP1.y + (xAxisLayer.low ? -settings.end : settings.end) 66 | let x2 = xScreenLoc 67 | let y2 = xAxisLayer.lineP1.y + (xAxisLayer.low ? settings.start : -settings.start) 68 | drawLine(context: context, color: settings.linesColor, width: settings.linesWidth, p1: CGPoint(x: x1, y: y1), p2: CGPoint(x: x2, y: y2)) 69 | } 70 | } 71 | 72 | if axis == .y || axis == .xAndY { 73 | for yValue in yValues { 74 | guard (settings.show?(yValue) ?? true) else {continue} 75 | 76 | let yScreenLoc = yAxisLayer.axis.screenLocForScalar(yValue) 77 | 78 | let x1 = yAxisLayer.lineP1.x + (yAxisLayer.low ? -settings.start : settings.start) 79 | let y1 = yScreenLoc 80 | let x2 = yAxisLayer.lineP1.x + (yAxisLayer.low ? settings.end : settings.end) 81 | let y2 = yScreenLoc 82 | drawLine(context: context, color: settings.linesColor, width: settings.linesWidth, p1: CGPoint(x: x1, y: y1), p2: CGPoint(x: x2, y: y2)) 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /SwiftCharts/Layers/ChartLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartLayer.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 02/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol ChartLayer { 12 | 13 | var chart: Chart? {get set} 14 | 15 | // Execute actions after chart initialisation, e.g. add subviews 16 | func chartInitialized(chart: Chart) 17 | 18 | // Draw directly in chart's context 19 | // Everything drawn here will appear behind subviews added by any layer (regardless of position in layers array) 20 | func chartViewDrawing(context: CGContext, chart: Chart) 21 | 22 | func chartContentViewDrawing(context: CGContext, chart: Chart) 23 | 24 | func chartDrawersContentViewDrawing(context: CGContext, chart: Chart, view: UIView) 25 | 26 | /// Trigger views update, to match updated model data 27 | func update() 28 | 29 | /// Handle a change of the available inner space caused by an axis change of size in a direction orthogonal to the axis. 30 | func handleAxisInnerFrameChange(_ xLow: ChartAxisLayerWithFrameDelta?, yLow: ChartAxisLayerWithFrameDelta?, xHigh: ChartAxisLayerWithFrameDelta?, yHigh: ChartAxisLayerWithFrameDelta?) 31 | 32 | func zoom(_ x: CGFloat, y: CGFloat, centerX: CGFloat, centerY: CGFloat) 33 | 34 | func zoom(_ scaleX: CGFloat, scaleY: CGFloat, centerX: CGFloat, centerY: CGFloat) 35 | 36 | func pan(_ deltaX: CGFloat, deltaY: CGFloat) 37 | 38 | func handlePanStart(_ location: CGPoint) 39 | 40 | func handlePanFinish() 41 | 42 | func handleZoomFinish() 43 | 44 | func handlePanEnd() 45 | 46 | func handleZoomEnd() 47 | 48 | @discardableResult 49 | func handleGlobalTap(_ location: CGPoint) -> Any? 50 | 51 | /// Return true to disable chart panning 52 | func processPan(location: CGPoint, deltaX: CGFloat, deltaY: CGFloat, isGesture: Bool, isDeceleration: Bool) -> Bool 53 | 54 | /// Return true to disable chart zooming 55 | func processZoom(deltaX: CGFloat, deltaY: CGFloat, anchorX: CGFloat, anchorY: CGFloat) -> Bool 56 | 57 | func keepInBoundaries() 58 | } -------------------------------------------------------------------------------- /SwiftCharts/Layers/ChartLayerBase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartLayerBase.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 02/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Convenience class to store common properties and make protocol's methods optional 12 | open class ChartLayerBase: NSObject, ChartLayer { 13 | 14 | open weak var chart: Chart? 15 | 16 | open func chartInitialized(chart: Chart) { 17 | self.chart = chart 18 | } 19 | 20 | open func chartViewDrawing(context: CGContext, chart: Chart) {} 21 | 22 | open func chartContentViewDrawing(context: CGContext, chart: Chart) {} 23 | 24 | open func chartDrawersContentViewDrawing(context: CGContext, chart: Chart, view: UIView) {} 25 | 26 | open func update() {} 27 | 28 | open func handleAxisInnerFrameChange(_ xLow: ChartAxisLayerWithFrameDelta?, yLow: ChartAxisLayerWithFrameDelta?, xHigh: ChartAxisLayerWithFrameDelta?, yHigh: ChartAxisLayerWithFrameDelta?) {} 29 | 30 | open func zoom(_ x: CGFloat, y: CGFloat, centerX: CGFloat, centerY: CGFloat) {} 31 | 32 | open func zoom(_ scaleX: CGFloat, scaleY: CGFloat, centerX: CGFloat, centerY: CGFloat) {} 33 | 34 | open func pan(_ deltaX: CGFloat, deltaY: CGFloat) {} 35 | 36 | open func processPan(location: CGPoint, deltaX: CGFloat, deltaY: CGFloat, isGesture: Bool, isDeceleration: Bool) -> Bool { 37 | return false 38 | } 39 | 40 | open func handlePanStart(_ location: CGPoint) {} 41 | 42 | open func handlePanFinish() {} 43 | 44 | open func handleZoomFinish() {} 45 | 46 | open func handlePanEnd() {} 47 | 48 | open func handleZoomEnd() {} 49 | 50 | open func processZoom(deltaX: CGFloat, deltaY: CGFloat, anchorX: CGFloat, anchorY: CGFloat) -> Bool { 51 | return false 52 | } 53 | 54 | open func handleGlobalTap(_ location: CGPoint) -> Any? { 55 | return nil 56 | } 57 | 58 | open func keepInBoundaries() {} 59 | 60 | public override init() {} 61 | } 62 | -------------------------------------------------------------------------------- /SwiftCharts/Layers/ChartPointsAreaLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPointsAreaLayer.swift 3 | // swiftCharts 4 | // 5 | // Created by ischuetz on 15/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartPointsAreaLayer: ChartPointsLayer { 12 | 13 | fileprivate let areaColors: [UIColor] 14 | fileprivate let animDuration: Float 15 | fileprivate let animDelay: Float 16 | fileprivate let pathGenerator: ChartLinesViewPathGenerator 17 | fileprivate let addContainerPoints: Bool 18 | fileprivate var areaViews: [UIView] = [] 19 | 20 | public init(xAxis: ChartAxis, yAxis: ChartAxis, chartPoints: [T], areaColors: [UIColor], animDuration: Float, animDelay: Float, addContainerPoints: Bool, pathGenerator: ChartLinesViewPathGenerator) { 21 | self.areaColors = areaColors 22 | self.animDuration = animDuration 23 | self.animDelay = animDelay 24 | self.pathGenerator = pathGenerator 25 | self.addContainerPoints = addContainerPoints 26 | super.init(xAxis: xAxis, yAxis: yAxis, chartPoints: chartPoints) 27 | } 28 | 29 | public convenience init(xAxis: ChartAxis, yAxis: ChartAxis, chartPoints: [T], areaColor: UIColor, animDuration: Float, animDelay: Float, addContainerPoints: Bool, pathGenerator: ChartLinesViewPathGenerator) { 30 | self.init(xAxis: xAxis, yAxis: yAxis, chartPoints: chartPoints, areaColors: [areaColor], animDuration: animDuration, animDelay: animDelay, addContainerPoints: addContainerPoints, pathGenerator: pathGenerator) 31 | } 32 | 33 | open override func display(chart: Chart) { 34 | let areaView = ChartAreasView(points: chartPointScreenLocs, frame: chart.bounds, colors: areaColors, animDuration: animDuration, animDelay: animDelay, addContainerPoints: addContainerPoints, pathGenerator: pathGenerator) 35 | areaViews.append(areaView) 36 | chart.addSubview(areaView) 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /SwiftCharts/Layers/ChartPointsBubbleLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPointsBubbleLayer.swift 3 | // Examples 4 | // 5 | // Created by ischuetz on 16/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartPointsBubbleLayer: ChartPointsLayer { 12 | 13 | fileprivate let diameterFactor: Double 14 | 15 | public init(xAxis: ChartAxis, yAxis: ChartAxis, chartPoints: [T], displayDelay: Float = 0, maxBubbleDiameter: Double = 30, minBubbleDiameter: Double = 2) { 16 | 17 | let (minDiameterScalar, maxDiameterScalar): (Double, Double) = chartPoints.reduce((min: 0, max: 0)) {tuple, chartPoint in 18 | (min: min(tuple.min, chartPoint.diameterScalar), max: max(tuple.max, chartPoint.diameterScalar)) 19 | } 20 | 21 | diameterFactor = (maxBubbleDiameter - minBubbleDiameter) / (maxDiameterScalar - minDiameterScalar) 22 | 23 | super.init(xAxis: xAxis, yAxis: yAxis, chartPoints: chartPoints, displayDelay: displayDelay) 24 | } 25 | 26 | override open func chartViewDrawing(context: CGContext, chart: Chart) { 27 | 28 | for chartPointModel in chartPointsModels { 29 | 30 | context.setLineWidth(1.0) 31 | context.setStrokeColor(chartPointModel.chartPoint.borderColor.cgColor) 32 | context.setFillColor(chartPointModel.chartPoint.bgColor.cgColor) 33 | 34 | let diameter = CGFloat(chartPointModel.chartPoint.diameterScalar * diameterFactor) 35 | let circleRect = (CGRect(x: chartPointModel.screenLoc.x - diameter / 2, y: chartPointModel.screenLoc.y - diameter / 2, width: diameter, height: diameter)) 36 | 37 | context.fillEllipse(in: circleRect) 38 | context.strokeEllipse(in: circleRect) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /SwiftCharts/Layers/ChartPointsCandleStickViewsLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPointsCandleStickViewsLayer.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 29/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartPointsCandleStickViewsLayer: ChartPointsViewsLayer { 12 | 13 | public init(xAxis: ChartAxis, yAxis: ChartAxis, innerFrame: CGRect, chartPoints: [T], viewGenerator: @escaping ChartPointViewGenerator) { 14 | super.init(xAxis: xAxis, yAxis: yAxis, chartPoints: chartPoints, viewGenerator: viewGenerator) 15 | } 16 | 17 | open func highlightChartpointView(screenLoc: CGPoint) { 18 | let x = screenLoc.x 19 | for viewWithChartPoint in viewsWithChartPoints { 20 | let view = viewWithChartPoint.view 21 | let originX = view.frame.origin.x 22 | view.highlighted = x > originX && x < originX + view.frame.width 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SwiftCharts/Layers/ChartPointsSingleViewLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPointsSingleViewLayer.swift 3 | // swift_charts 4 | // 5 | // Created by ischuetz on 19/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // Layer that shows only one view at a time 12 | open class ChartPointsSingleViewLayer: ChartPointsViewsLayer { 13 | 14 | fileprivate var addedViews: [UIView] = [] 15 | 16 | fileprivate var activeChartPoint: T? 17 | 18 | public init(xAxis: ChartAxis, yAxis: ChartAxis, innerFrame: CGRect, chartPoints: [T], viewGenerator: @escaping ChartPointViewGenerator, mode: ChartPointsViewsLayerMode = .scaleAndTranslate, keepOnFront: Bool = true) { 19 | super.init(xAxis: xAxis, yAxis: yAxis, chartPoints: chartPoints, viewGenerator: viewGenerator, mode: mode, keepOnFront: keepOnFront) 20 | } 21 | 22 | override open func display(chart: Chart) { 23 | // skip adding views - this layer manages its own list 24 | } 25 | 26 | open func showView(chartPoint: T, chart: Chart) { 27 | 28 | activeChartPoint = chartPoint 29 | 30 | for view in addedViews { 31 | view.removeFromSuperview() 32 | } 33 | 34 | let screenLoc = chartPointScreenLoc(chartPoint) 35 | let index = chartPointsModels.map{$0.chartPoint}.firstIndex(of: chartPoint)! 36 | let model: ChartPointLayerModel = ChartPointLayerModel(chartPoint: chartPoint, index: index, screenLoc: screenLoc) 37 | if let view = viewGenerator(model, self, chart) { 38 | addedViews.append(view) 39 | addSubview(chart, view: view) 40 | 41 | viewsWithChartPoints = [ViewWithChartPoint(view: view, chartPointModel: model)] 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SwiftCharts/Layers/ChartPointsTrackerLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPointsTrackerLayer.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 29/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartPointsTrackerLayer: ChartPointsLayer { 12 | 13 | fileprivate var view: TrackerView? 14 | fileprivate let locChangedFunc: ((CGPoint) -> ()) 15 | 16 | fileprivate let lineColor: UIColor 17 | fileprivate let lineWidth: CGFloat 18 | 19 | fileprivate lazy private(set) var currentPositionLineOverlay: UIView = { 20 | let currentPositionLineOverlay = UIView() 21 | currentPositionLineOverlay.backgroundColor = self.lineColor 22 | return currentPositionLineOverlay 23 | }() 24 | 25 | 26 | public init(xAxis: ChartAxis, yAxis: ChartAxis, chartPoints: [T], locChangedFunc: @escaping (CGPoint) -> (), lineColor: UIColor = UIColor.black, lineWidth: CGFloat = 1) { 27 | self.locChangedFunc = locChangedFunc 28 | self.lineColor = lineColor 29 | self.lineWidth = lineWidth 30 | super.init(xAxis: xAxis, yAxis: yAxis, chartPoints: chartPoints) 31 | } 32 | 33 | open override func display(chart: Chart) { 34 | let view = TrackerView(frame: chart.bounds, updateFunc: {[weak self] location in 35 | self?.locChangedFunc(location) 36 | self?.currentPositionLineOverlay.center.x = location.x 37 | }) 38 | view.isUserInteractionEnabled = true 39 | chart.addSubview(view) 40 | self.view = view 41 | 42 | view.addSubview(currentPositionLineOverlay) 43 | 44 | currentPositionLineOverlay.frame = CGRect(x: chart.containerFrame.origin.x + 200 - lineWidth / 2, y: modelLocToScreenLoc(y: yAxis.last), width: lineWidth, height: modelLocToScreenLoc(y: yAxis.first)) 45 | } 46 | } 47 | 48 | 49 | private class TrackerView: UIView { 50 | 51 | let updateFunc: ((CGPoint) -> ())? 52 | 53 | init(frame: CGRect, updateFunc: @escaping (CGPoint) -> ()) { 54 | self.updateFunc = updateFunc 55 | 56 | super.init(frame: frame) 57 | } 58 | 59 | required init(coder aDecoder: NSCoder) { 60 | fatalError("init(coder:) has not been implemented") 61 | } 62 | 63 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 64 | let touch = touches.first! 65 | let location = touch.location(in: self) 66 | 67 | self.updateFunc?(location) 68 | } 69 | 70 | override func touchesMoved(_ touches: Set, with event: UIEvent?) { 71 | let touch = touches.first! 72 | let location = touch.location(in: self) 73 | 74 | self.updateFunc?(location) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /SwiftCharts/Layers/ChartShowCoordsLinesLayer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartShowCoordsLinesLayer.swift 3 | // swift_charts 4 | // 5 | // Created by ischuetz on 19/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // TODO improve behaviour during zooming and panning - line has to be only translated, not scaled. Needs .translate behaviour of ChartPointsViewsLayer, maybe we can extend this layer? 12 | open class ChartShowCoordsLinesLayer: ChartPointsLayer { 13 | 14 | fileprivate var view: UIView? 15 | 16 | fileprivate var activeChartPoint: T? 17 | 18 | public init(xAxis: ChartAxis, yAxis: ChartAxis, chartPoints: [T]) { 19 | super.init(xAxis: xAxis, yAxis: yAxis, chartPoints: chartPoints) 20 | } 21 | 22 | open func showChartPointLines(_ chartPoint: T, chart: Chart) { 23 | 24 | if let view = self.view { 25 | 26 | activeChartPoint = chartPoint 27 | 28 | for v in view.subviews { 29 | v.removeFromSuperview() 30 | } 31 | 32 | let screenLoc = chartPointScreenLoc(chartPoint) 33 | 34 | let hLine = UIView(frame: CGRect(x: screenLoc.x, y: screenLoc.y, width: 0, height: 1)) 35 | let vLine = UIView(frame: CGRect(x: screenLoc.x, y: screenLoc.y, width: 0, height: 1)) 36 | 37 | for lineView in [hLine, vLine] { 38 | lineView.backgroundColor = UIColor.black 39 | view.addSubview(lineView) 40 | } 41 | 42 | func animations() { 43 | let axisOriginX = modelLocToScreenLoc(x: xAxis.first) 44 | let axisOriginY = modelLocToScreenLoc(y: yAxis.first) 45 | let axisLengthY = axisOriginY - modelLocToScreenLoc(y: yAxis.last) 46 | 47 | hLine.frame = CGRect(x: axisOriginX, y: screenLoc.y, width: screenLoc.x - axisOriginX, height: 1) 48 | vLine.frame = CGRect(x: screenLoc.x, y: screenLoc.y, width: 1, height: axisLengthY - screenLoc.y) 49 | } 50 | 51 | UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut, animations: { 52 | animations() 53 | }, completion: nil) 54 | } 55 | } 56 | 57 | override open func display(chart: Chart) { 58 | let view = UIView(frame: chart.bounds) 59 | view.isUserInteractionEnabled = true 60 | chart.addSubview(view) 61 | self.view = view 62 | } 63 | 64 | open override func zoom(_ x: CGFloat, y: CGFloat, centerX: CGFloat, centerY: CGFloat) { 65 | super.zoom(x, y: y, centerX: centerX, centerY: centerY) 66 | updateChartPointsScreenLocations() 67 | } 68 | 69 | open override func pan(_ deltaX: CGFloat, deltaY: CGFloat) { 70 | super.pan(deltaX, deltaY: deltaY) 71 | updateChartPointsScreenLocations() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /SwiftCharts/NSDate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 05/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | var systemCalendar: Calendar { 12 | return Calendar.current 13 | } 14 | 15 | extension Date { 16 | 17 | var day: Int { 18 | return systemCalendar.dateComponents([.day], from: self).day! 19 | } 20 | 21 | var month: Int { 22 | return systemCalendar.dateComponents([.month], from: self).month! 23 | } 24 | 25 | var year: Int { 26 | return systemCalendar.dateComponents([.year], from: self).year! 27 | } 28 | 29 | func components(_ unitFlags: Set) -> DateComponents { 30 | return systemCalendar.dateComponents(unitFlags, from: self) 31 | } 32 | 33 | func component(_ unit: Calendar.Component) -> Int { 34 | let components = systemCalendar.dateComponents([unit], from: self) 35 | return components.value(for: unit)! 36 | } 37 | 38 | func addComponent(_ value: Int, unit: Calendar.Component) -> Date { 39 | var dateComponents = DateComponents() 40 | dateComponents.setValue(value, for: unit) 41 | return systemCalendar.date(byAdding: dateComponents, to: self)! 42 | } 43 | 44 | static func toDateComponents(_ timeInterval: TimeInterval, unit: Calendar.Component) -> DateComponents { 45 | let date1 = Date() 46 | let date2 = Date(timeInterval: timeInterval, since: date1) 47 | return systemCalendar.dateComponents([unit], from: date1, to: date2) 48 | } 49 | 50 | func timeInterval(_ date: Date, unit: Calendar.Component) -> Int { 51 | let interval = timeIntervalSince(date) 52 | let components = Date.toDateComponents(interval, unit: unit) 53 | return components.value(for: unit)! 54 | } 55 | } -------------------------------------------------------------------------------- /SwiftCharts/Optional.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Optional.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 06/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension Optional where Wrapped: Comparable { 12 | 13 | func minOpt(_ other: Wrapped?) -> Wrapped? { 14 | return anyOrF(other) {min($0, $1)} 15 | } 16 | 17 | func maxOpt(_ other: Wrapped?) -> Wrapped? { 18 | return anyOrF(other) {max($0, $1)} 19 | } 20 | 21 | func minOpt(_ other: Wrapped) -> Wrapped { 22 | return anyOrF(other) {min($0, $1)}! 23 | } 24 | 25 | func maxOpt(_ other: Wrapped) -> Wrapped { 26 | return anyOrF(other) {max($0, $1)}! 27 | } 28 | 29 | fileprivate func anyOrF(_ other: Wrapped?, f: (Wrapped, Wrapped) -> Wrapped?) -> Wrapped? { 30 | switch (self, other) { 31 | case (nil, nil): return nil 32 | case (let value, nil): return value 33 | case (nil, let value): return value 34 | case (let value1, let value2): return f(value1!, value2!) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SwiftCharts/Pannable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Pannable.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 05/07/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol Pannable { 12 | 13 | var zoomPanSettings: ChartSettingsZoomPan {get} 14 | 15 | var containerView: UIView {get} 16 | 17 | var contentView: UIView {get} 18 | 19 | var transX: CGFloat {get} 20 | var transY: CGFloat {get} 21 | 22 | func pan(x: CGFloat, y: CGFloat, elastic: Bool) 23 | 24 | func pan(deltaX: CGFloat, deltaY: CGFloat, elastic: Bool) 25 | 26 | func pan(deltaX: CGFloat, deltaY: CGFloat, isGesture: Bool, isDeceleration: Bool, elastic: Bool) 27 | 28 | func onPanStart(deltaX: CGFloat, deltaY: CGFloat) 29 | 30 | func onPanFinish(transX: CGFloat, transY: CGFloat, deltaX: CGFloat, deltaY: CGFloat, isGesture: Bool, isDeceleration: Bool) 31 | } 32 | 33 | public extension Pannable { 34 | 35 | func pan(x: CGFloat, y: CGFloat, elastic: Bool) { 36 | pan(deltaX: x - transX, deltaY: transY - y, isGesture: false, isDeceleration: false, elastic: elastic) 37 | } 38 | 39 | func pan(deltaX: CGFloat, deltaY: CGFloat, elastic: Bool) { 40 | pan(deltaX: deltaX, deltaY: deltaY, isGesture: false, isDeceleration: false, elastic: elastic) 41 | } 42 | 43 | func pan(deltaX: CGFloat, deltaY: CGFloat, isGesture: Bool, isDeceleration: Bool, elastic: Bool) { 44 | 45 | onPanStart(deltaX: deltaX, deltaY: deltaY) 46 | 47 | if !elastic { 48 | func maxTX(_ minXLimit: CGFloat) -> CGFloat { 49 | return minXLimit - (contentView.frame.minX - contentView.transform.tx) 50 | } 51 | func maxTY(_ minYLimit: CGFloat) -> CGFloat { 52 | return minYLimit - (contentView.frame.minY - contentView.transform.ty) 53 | } 54 | contentView.transform.tx = max(maxTX(containerView.frame.width - contentView.frame.width), min(maxTX(0), contentView.transform.tx + deltaX)) 55 | contentView.transform.ty = max(maxTY(containerView.frame.height - contentView.frame.height), min(maxTY(0), contentView.transform.ty + deltaY)) 56 | 57 | } else { 58 | contentView.transform.tx = contentView.transform.tx + deltaX 59 | contentView.transform.ty = contentView.transform.ty + deltaY 60 | } 61 | 62 | onPanFinish(transX: transX, transY: transY, deltaX: deltaX, deltaY: deltaY, isGesture: isGesture, isDeceleration: isDeceleration) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /SwiftCharts/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 13/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension String { 12 | 13 | subscript (i: Int) -> Character { 14 | return self[index(startIndex, offsetBy: i)] 15 | } 16 | 17 | subscript (i: Int) -> String { 18 | return String(self[i] as Character) 19 | } 20 | 21 | subscript (r: Range) -> String { 22 | let start = index(startIndex, offsetBy: r.lowerBound) 23 | // let end = <#T##String.CharacterView corresponding to `start`##String.CharacterView#>.index(start, offsetBy: r.upperBound - r.lowerBound) 24 | let end = index(start, offsetBy: r.upperBound - r.lowerBound) 25 | return String(self[start.. CGSize { 29 | #if swift(>=4) 30 | return NSAttributedString(string: self, attributes: [NSAttributedString.Key.font: font]).size() 31 | #else 32 | return NSAttributedString(string: self, attributes: [NSFontAttributeName: font]).size() 33 | #endif 34 | } 35 | 36 | func width(_ font: UIFont) -> CGFloat { 37 | return size(font).width 38 | } 39 | 40 | func height(_ font: UIFont) -> CGFloat { 41 | return size(font).height 42 | } 43 | 44 | func trim() -> String { 45 | return trimmingCharacters(in: CharacterSet.whitespaces) 46 | } 47 | 48 | func fittingSubstring(_ width: CGFloat, font: UIFont) -> String { 49 | for i in stride(from: count, to: 0, by: -1) { 50 | let substr = self[0.. String { 59 | let ellipsis = "..." 60 | let substr = fittingSubstring(width - ellipsis.width(font), font: font) 61 | if substr.count != count { 62 | return "\(substr.trim())\(ellipsis)" 63 | } else { 64 | return self 65 | } 66 | } 67 | 68 | // TODO Doesn't work - why? 69 | // func fittingSubstring(width: CGFloat, font: UIFont) -> String { 70 | // let fontRef = CTFontCreateWithName(font.fontName, font.pointSize, nil) 71 | // 72 | // let attributes: [String: AnyObject] = [String(kCTFontAttributeName): fontRef] 73 | //// let attributes: [String: AnyObject] = [NSFontAttributeName : fontRef] 74 | // 75 | // let attributedString = NSAttributedString(string: self, attributes: attributes) 76 | // 77 | // let frameSetterRef = CTFramesetterCreateWithAttributedString(attributedString) 78 | // 79 | // var characterFitRange = CFRangeMake(0, 0) 80 | // CTFramesetterSuggestFrameSizeWithConstraints(frameSetterRef, CFRangeMake(0, 0), nil, CGSizeMake(width, font.lineHeight), &characterFitRange) 81 | // 82 | // let characterCount = characterFitRange.length 83 | // 84 | // return substringToIndex(startIndex.advancedBy(characterCount)) 85 | // } 86 | } 87 | -------------------------------------------------------------------------------- /SwiftCharts/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.2.4 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SwiftCharts/Supporting Files/SwiftCharts.h: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftCharts.h 3 | // SwiftCharts 4 | // 5 | // Created by Pierre-Marc Airoldi on 2015-08-23. 6 | // Copyright (c) 2015 Pierre-Marc Airoldi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for SwiftCharts. 12 | FOUNDATION_EXPORT double SwiftChartsVersionNumber; 13 | 14 | //! Project version string for SwiftCharts. 15 | FOUNDATION_EXPORT const unsigned char SwiftChartsVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /SwiftCharts/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 24/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | 13 | var alpha: CGFloat { 14 | return components.alpha 15 | } 16 | 17 | var components: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { 18 | var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0 19 | getRed(&r, green: &g, blue: &b, alpha: &a) 20 | return (red: r, green: g, blue: b, alpha: a) 21 | } 22 | 23 | func adjustBrigtness(factor brightnessFactor: CGFloat) -> UIColor { 24 | var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0 25 | guard getHue(&h, saturation: &s, brightness: &b, alpha: &a) else {return self} 26 | 27 | return UIColor(hue: h, saturation: s, brightness: min(b * brightnessFactor, 1), alpha: a) 28 | } 29 | 30 | func copy(_ red: CGFloat? = nil, green: CGFloat? = nil, blue: CGFloat? = nil, alpha: CGFloat? = nil) -> UIColor { 31 | let components = self.components 32 | return UIColor(red: red ?? components.red, green: green ?? components.green, blue: blue ?? components.blue, alpha: alpha ?? components.alpha) 33 | } 34 | } -------------------------------------------------------------------------------- /SwiftCharts/Utils/ChartNiceNumberCalculator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartNiceNumberCalculator.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 08/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ChartNiceNumberCalculator { 12 | 13 | // src: http://stackoverflow.com/a/4948320/930450 14 | static func niceNumber(_ value: Double, round: Bool) -> Double { 15 | 16 | let exponent = floor(log10(value)) 17 | 18 | let fraction = value / pow(10, exponent) 19 | 20 | var niceFraction = 1.0 21 | 22 | if round { 23 | if fraction < 1.5 { 24 | niceFraction = 1 25 | } else if fraction < 3 { 26 | niceFraction = 2 27 | } else if fraction < 7 { 28 | niceFraction = 5 29 | } else { 30 | niceFraction = 10 31 | } 32 | 33 | } else { 34 | if fraction <= 1 { 35 | niceFraction = 1 36 | } else if fraction <= 2 { 37 | niceFraction = 2 38 | } else if niceFraction <= 5 { 39 | niceFraction = 5 40 | } else { 41 | niceFraction = 10 42 | } 43 | } 44 | 45 | return niceFraction * pow(10, exponent) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /SwiftCharts/Utils/ChartTextUtils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartTextUtils.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 13/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct ChartTextUtils { 12 | 13 | public static func maxTextWidth(_ minValue: Double, maxValue: Double, formatter: NumberFormatter, font: UIFont) -> CGFloat { 14 | 15 | let noDecimalsFormatter = NumberFormatter() 16 | noDecimalsFormatter.maximumFractionDigits = 0 17 | noDecimalsFormatter.roundingMode = .down 18 | 19 | let noDecimalsMin = noDecimalsFormatter.string(from: NSNumber(value: minValue))! 20 | let noDecimalsMax = noDecimalsFormatter.string(from: NSNumber(value: maxValue))! 21 | 22 | let minNumberNoDecimalsTextSize = noDecimalsMin.width(font) 23 | let maxNumberNoDecimalsTextSize = noDecimalsMax.width(font) 24 | 25 | let maxNoDecimalsLength = max(minNumberNoDecimalsTextSize, maxNumberNoDecimalsTextSize) 26 | 27 | let digitMaxWidth = "8".width(font) 28 | let maxDecimalsWidth = CGFloat(formatter.maximumFractionDigits) * digitMaxWidth 29 | 30 | let widthForDecimalSign = formatter.maximumFractionDigits > 0 ? formatter.decimalSeparator.width(font) : 0 31 | 32 | return maxNoDecimalsLength + maxDecimalsWidth + widthForDecimalSign 33 | } 34 | 35 | public static func maxTextHeight(_ minValue: Double, maxValue: Double, formatter: NumberFormatter, font: UIFont) -> CGFloat { 36 | return "H".height(font) 37 | } 38 | } -------------------------------------------------------------------------------- /SwiftCharts/Utils/ChartTimeUtils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartTimeUtils.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 13/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct ChartTimeUtils { 12 | 13 | /** 14 | Converts seconds to the same amount as a dispatch_time_t 15 | 16 | - parameter secs: The number of seconds 17 | 18 | - returns: The number of seconds as a dispatch_time_t 19 | */ 20 | static func toDispatchTime(_ secs: Float) -> DispatchTime { 21 | return DispatchTime.now() + Double(Int64(Double(secs) * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SwiftCharts/Utils/ChartViewSelector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartViewSelector.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 24/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Updates a UIView for selected / deselected state 12 | public protocol ChartViewSelector { 13 | 14 | func displaySelected(_ view: UIView, selected: Bool) 15 | } 16 | -------------------------------------------------------------------------------- /SwiftCharts/Utils/ChartViewSelectorAlpha.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartViewSelectorAlpha.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 25/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartViewSelectorAlpha: ChartViewSelector { 12 | 13 | fileprivate var selectedAlpha: CGFloat 14 | fileprivate var deselectedAlpha: CGFloat 15 | 16 | public init(selectedAlpha: CGFloat, deselectedAlpha: CGFloat) { 17 | self.selectedAlpha = selectedAlpha 18 | self.deselectedAlpha = deselectedAlpha 19 | } 20 | 21 | open func displaySelected(_ view: UIView, selected: Bool) { 22 | view.backgroundColor = view.backgroundColor.map{$0.copy(alpha: selected ? selectedAlpha : deselectedAlpha)} 23 | } 24 | } -------------------------------------------------------------------------------- /SwiftCharts/Utils/ChartViewSelectorBrightness.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartViewSelectorBrightness.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 25/08/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartViewSelectorBrightness: ChartViewSelector { 12 | 13 | let selectedFactor: CGFloat 14 | 15 | public init(selectedFactor: CGFloat) { 16 | self.selectedFactor = selectedFactor 17 | } 18 | 19 | open func displaySelected(_ view: UIView, selected: Bool) { 20 | view.backgroundColor = selected ? view.backgroundColor?.adjustBrigtness(factor: selectedFactor) : view.backgroundColor?.adjustBrigtness(factor: 1 / selectedFactor) 21 | } 22 | } -------------------------------------------------------------------------------- /SwiftCharts/Utils/Operators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Operators.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 16/07/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreGraphics 11 | 12 | infix operator =~ : ComparisonPrecedence 13 | 14 | func =~ (a: Float, b: Float) -> Bool { 15 | return fabsf(a - b) < Float.ulpOfOne 16 | } 17 | 18 | func =~ (a: CGFloat, b: CGFloat) -> Bool { 19 | return abs(a - b) < CGFloat.ulpOfOne 20 | } 21 | 22 | func =~ (a: Double, b: Double) -> Bool { 23 | return fabs(a - b) < Double.ulpOfOne 24 | } 25 | 26 | infix operator !=~ : ComparisonPrecedence 27 | 28 | func !=~ (a: Float, b: Float) -> Bool { 29 | return !(a =~ b) 30 | } 31 | 32 | func !=~ (a: CGFloat, b: CGFloat) -> Bool { 33 | return !(a =~ b) 34 | } 35 | 36 | func !=~ (a: Double, b: Double) -> Bool { 37 | return !(a =~ b) 38 | } 39 | 40 | infix operator <=~ : ComparisonPrecedence 41 | 42 | func <=~ (a: Float, b: Float) -> Bool { 43 | return a =~ b || a < b 44 | } 45 | 46 | func <=~ (a: CGFloat, b: CGFloat) -> Bool { 47 | return a =~ b || a < b 48 | } 49 | 50 | func <=~ (a: Double, b: Double) -> Bool { 51 | return a =~ b || a < b 52 | } 53 | 54 | infix operator >=~ : ComparisonPrecedence 55 | 56 | func >=~ (a: Float, b: Float) -> Bool { 57 | return a =~ b || a > b 58 | } 59 | 60 | func >=~ (a: CGFloat, b: CGFloat) -> Bool { 61 | return a =~ b || a > b 62 | } 63 | 64 | func >=~ (a: Double, b: Double) -> Bool { 65 | return a =~ b || a > b 66 | } 67 | -------------------------------------------------------------------------------- /SwiftCharts/Utils/Trendlines/TrendlineGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrendlineGenerator.swift 3 | // Examples 4 | // 5 | // Created by ischuetz on 03/08/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct TrendlineGenerator { 12 | 13 | public static func trendline(_ chartPoints: [ChartPoint]) -> [ChartPoint] { 14 | 15 | guard chartPoints.count > 1 else {return []} 16 | 17 | let doubleO: Double = 0 18 | let (sumX, sumY, sumXY, sumXX): (sumX: Double, sumY: Double, sumXY: Double, sumXX: Double) = chartPoints.reduce((sumX: doubleO, sumY: doubleO, sumXY: doubleO, sumXX: doubleO)) {(tuple, chartPoint) in 19 | 20 | let x: Double = chartPoint.x.scalar 21 | let y: Double = chartPoint.y.scalar 22 | 23 | return ( 24 | tuple.sumX + x, 25 | tuple.sumY + y, 26 | tuple.sumXY + x * y, 27 | tuple.sumXX + x * x 28 | ) 29 | } 30 | 31 | let count = Double(chartPoints.count) 32 | 33 | let b = (count * sumXY - sumX * sumY) / (count * sumXX - sumX * sumX) 34 | let a = (sumY - b * sumX) / count 35 | 36 | // equation of line: y = a + bx 37 | func y(_ x: Double) -> Double { 38 | return a + b * x 39 | } 40 | 41 | let first = chartPoints.first! 42 | let last = chartPoints.last! 43 | 44 | return [ 45 | ChartPoint(x: ChartAxisValueDouble(first.x.scalar), y: ChartAxisValueDouble(y(first.x.scalar))), 46 | ChartPoint(x: ChartAxisValueDouble(last.x.scalar), y: ChartAxisValueDouble(y(last.x.scalar))) 47 | ] 48 | } 49 | } -------------------------------------------------------------------------------- /SwiftCharts/Views/CatmullPathGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CatmullPathGenerator.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 22/09/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class CatmullPathGenerator: ChartLinesViewPathGenerator { 12 | 13 | public init() {} 14 | 15 | open func generatePath(points: [CGPoint], lineWidth: CGFloat) -> UIBezierPath { 16 | guard let last = points.last else {return UIBezierPath()} 17 | return UIBezierPath(catmullRomPoints: points + [last.offset(x: 1)], alpha: 0.5) ?? CubicLinePathGenerator(tension1: 0.2, tension2: 0.2).generatePath(points: points, lineWidth: lineWidth) 18 | } 19 | 20 | open func generateAreaPath(points: [CGPoint], lineWidth: CGFloat) -> UIBezierPath { 21 | return generatePath(points: points, lineWidth: lineWidth) 22 | } 23 | } 24 | 25 | // src: https://github.com/andrelind/swift-catmullrom/blob/master/CatmullRom.swift (modified) 26 | private extension UIBezierPath { 27 | 28 | convenience init?(catmullRomPoints: [CGPoint], alpha: CGFloat) { 29 | self.init() 30 | 31 | if catmullRomPoints.count < 4 { 32 | return nil 33 | } 34 | 35 | let startIndex = 0 36 | let endIndex = catmullRomPoints.count - 2 37 | 38 | for i in startIndex ..< endIndex { 39 | let prevIndex = i - 1 < 0 ? catmullRomPoints.count - 1 : i - 1 40 | let nextIndex = i + 1 % catmullRomPoints.count 41 | let nextNextIndex = nextIndex + 1 % catmullRomPoints.count 42 | 43 | let p0 = catmullRomPoints[prevIndex] 44 | let p1 = catmullRomPoints[i] 45 | let p2 = catmullRomPoints[nextIndex] 46 | let p3 = catmullRomPoints[nextNextIndex] 47 | 48 | let d1 = p1.substract(p0).length() 49 | let d2 = p2.substract(p1).length() 50 | let d3 = p3.substract(p2).length() 51 | 52 | var b1 = p2.multiplyBy(pow(d1, 2 * alpha)) 53 | b1 = b1.substract(p0.multiplyBy(pow(d2, 2 * alpha))) 54 | b1 = b1.add(p1.multiplyBy(2 * pow(d1, 2 * alpha) + 3 * pow(d1, alpha) * pow(d2, alpha) + pow(d2, 2 * alpha))) 55 | b1 = b1.multiplyBy(1.0 / (3 * pow(d1, alpha) * (pow(d1, alpha) + pow(d2, alpha)))) 56 | 57 | var b2 = p1.multiplyBy(pow(d3, 2 * alpha)) 58 | b2 = b2.substract(p3.multiplyBy(pow(d2, 2 * alpha))) 59 | b2 = b2.add(p2.multiplyBy(2 * pow(d3, 2 * alpha) + 3 * pow(d3, alpha) * pow(d2, alpha) + pow(d2, 2 * alpha))) 60 | b2 = b2.multiplyBy(1.0 / (3 * pow(d3, alpha) * (pow(d3, alpha) + pow(d2, alpha)))) 61 | 62 | if i == startIndex { 63 | move(to: p1) 64 | } 65 | 66 | addCurve(to: p2, controlPoint1: b1, controlPoint2: b2) 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /SwiftCharts/Views/ChartCandleStickView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartCandleStickView.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 29/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartCandleStickView: UIView { 12 | 13 | fileprivate let innerRect: CGRect 14 | 15 | fileprivate let fillColor: UIColor 16 | fileprivate let strokeColor: UIColor 17 | 18 | fileprivate var currentFillColor: UIColor 19 | fileprivate var currentStrokeColor: UIColor 20 | 21 | fileprivate let highlightColor = UIColor.red 22 | 23 | fileprivate let strokeWidth: CGFloat 24 | 25 | var highlighted: Bool = false { 26 | didSet { 27 | if highlighted { 28 | currentFillColor = highlightColor 29 | currentStrokeColor = highlightColor 30 | } else { 31 | currentFillColor = fillColor 32 | currentStrokeColor = strokeColor 33 | } 34 | setNeedsDisplay() 35 | } 36 | } 37 | 38 | public init(lineX: CGFloat, width: CGFloat, top: CGFloat, bottom: CGFloat, innerRectTop: CGFloat, innerRectBottom: CGFloat, fillColor: UIColor, strokeColor: UIColor = UIColor.black, strokeWidth: CGFloat = 1) { 39 | 40 | let frameX = lineX - width / CGFloat(2) 41 | let frame = CGRect(x: frameX, y: top, width: width, height: bottom - top) 42 | let t = innerRectTop - top 43 | let hsw = strokeWidth / 2 44 | innerRect = CGRect(x: hsw, y: t + hsw, width: width - strokeWidth, height: innerRectBottom - top - t - strokeWidth) 45 | 46 | self.fillColor = fillColor 47 | self.strokeColor = strokeColor 48 | 49 | self.currentFillColor = fillColor 50 | self.currentStrokeColor = strokeColor 51 | self.strokeWidth = strokeWidth 52 | 53 | super.init(frame: frame) 54 | 55 | backgroundColor = UIColor.clear 56 | } 57 | 58 | required public init(coder aDecoder: NSCoder) { 59 | fatalError("init(coder:) has not been implemented") 60 | } 61 | 62 | override open func draw(_ rect: CGRect) { 63 | guard let context = UIGraphicsGetCurrentContext() else {return} 64 | 65 | let wHalf = frame.width / 2 66 | 67 | context.setLineWidth(strokeWidth) 68 | context.setStrokeColor(currentStrokeColor.cgColor) 69 | context.move(to: CGPoint(x: wHalf, y: 0)) 70 | context.addLine(to: CGPoint(x: wHalf, y: frame.height)) 71 | 72 | context.strokePath() 73 | 74 | context.setFillColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.0) 75 | context.setFillColor(currentFillColor.cgColor) 76 | context.fill(innerRect) 77 | context.stroke(innerRect) 78 | } 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /SwiftCharts/Views/ChartPointEllipseView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPointEllipseView.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 19/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartPointEllipseView: UIView { 12 | 13 | open var fillColor: UIColor = UIColor.gray 14 | open var borderColor: UIColor? = nil 15 | open var borderWidth: CGFloat? = nil 16 | open var animDelay: Float = 0 17 | open var animDuration: Float = 0 18 | open var animateSize: Bool = true 19 | open var animateAlpha: Bool = true 20 | open var animDamping: CGFloat = 1 21 | open var animInitSpringVelocity: CGFloat = 1 22 | 23 | open var touchHandler: (() -> ())? 24 | 25 | convenience public init(center: CGPoint, diameter: CGFloat) { 26 | self.init(center: center, width: diameter, height: diameter) 27 | } 28 | 29 | public init(center: CGPoint, width: CGFloat, height: CGFloat) { 30 | super.init(frame: CGRect(x: center.x - width / 2, y: center.y - height / 2, width: width, height: height)) 31 | self.backgroundColor = UIColor.clear 32 | } 33 | 34 | required public init(coder aDecoder: NSCoder) { 35 | fatalError("init(coder:) has not been implemented") 36 | } 37 | 38 | override open func didMoveToSuperview() { 39 | if animDuration != 0 { 40 | if animateSize { 41 | transform = CGAffineTransform(scaleX: 0.1, y: 0.1) 42 | } 43 | if animateAlpha { 44 | alpha = 0 45 | } 46 | 47 | UIView.animate(withDuration: TimeInterval(animDuration), delay: TimeInterval(animDelay), usingSpringWithDamping: animDamping, initialSpringVelocity: animInitSpringVelocity, options: UIView.AnimationOptions(), animations: { 48 | if self.animateSize { 49 | self.transform = CGAffineTransform(scaleX: 1, y: 1) 50 | } 51 | if self.animateAlpha { 52 | self.alpha = 1 53 | } 54 | }, completion: nil) 55 | } 56 | } 57 | 58 | override open func draw(_ rect: CGRect) { 59 | guard let context = UIGraphicsGetCurrentContext() else {return} 60 | 61 | let borderOffset = borderWidth ?? 0 62 | let circleRect = (CGRect(x: borderOffset, y: borderOffset, width: frame.size.width - (borderOffset * 2), height: frame.size.height - (borderOffset * 2))) 63 | 64 | if let borderWidth = borderWidth, let borderColor = borderColor { 65 | context.setLineWidth(borderWidth) 66 | context.setStrokeColor(borderColor.cgColor) 67 | context.strokeEllipse(in: circleRect) 68 | } 69 | context.setFillColor(fillColor.cgColor) 70 | context.fillEllipse(in: circleRect) 71 | } 72 | 73 | override open func touchesEnded(_ touches: Set, with event: UIEvent?) { 74 | touchHandler?() 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /SwiftCharts/Views/ChartPointTargetingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPointTargetingView.swift 3 | // swift_charts 4 | // 5 | // Created by ischuetz on 15/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartPointTargetingView: UIView { 12 | 13 | fileprivate let animDuration: Float 14 | fileprivate let animDelay: Float 15 | 16 | fileprivate let lineHorizontal: UIView 17 | fileprivate let lineVertical: UIView 18 | 19 | fileprivate let lineWidth = 1 20 | 21 | fileprivate let lineHorizontalTargetFrame: CGRect 22 | fileprivate let lineVerticalTargetFrame: CGRect 23 | 24 | public init(chartPoint: ChartPoint, screenLoc: CGPoint, animDuration: Float, animDelay: Float, layer: ChartCoordsSpaceLayer, chart: Chart) { 25 | self.animDuration = animDuration 26 | self.animDelay = animDelay 27 | 28 | let axisOriginX = layer.modelLocToScreenLoc(x: layer.xAxis.first) 29 | let axisOriginY = layer.modelLocToScreenLoc(y: layer.yAxis.last) 30 | let axisLengthX = layer.modelLocToScreenLoc(x: layer.xAxis.last) - axisOriginX 31 | let axisLengthY = abs(axisOriginY - layer.modelLocToScreenLoc(y: layer.yAxis.first)) 32 | 33 | lineHorizontal = UIView(frame: CGRect(x: axisOriginX, y: axisOriginY, width: axisLengthX, height: CGFloat(lineWidth))) 34 | lineVertical = UIView(frame: CGRect(x: axisOriginX, y: axisOriginY, width: CGFloat(lineWidth), height: axisLengthY)) 35 | 36 | lineHorizontal.backgroundColor = UIColor.black 37 | lineVertical.backgroundColor = UIColor.red 38 | 39 | let lineWidthHalf = lineWidth / 2 40 | var targetFrameH = lineHorizontal.frame 41 | targetFrameH.origin.y = screenLoc.y - CGFloat(lineWidthHalf) 42 | lineHorizontalTargetFrame = targetFrameH 43 | var targetFrameV = lineVertical.frame 44 | targetFrameV.origin.x = screenLoc.x - CGFloat(lineWidthHalf) 45 | lineVerticalTargetFrame = targetFrameV 46 | 47 | super.init(frame: chart.bounds) 48 | } 49 | 50 | required public init(coder aDecoder: NSCoder) { 51 | fatalError("init(coder:) has not been implemented") 52 | } 53 | 54 | override open func didMoveToSuperview() { 55 | addSubview(lineHorizontal) 56 | addSubview(lineVertical) 57 | 58 | func targetState() { 59 | lineHorizontal.frame = lineHorizontalTargetFrame 60 | lineVertical.frame = lineVerticalTargetFrame 61 | } 62 | 63 | if animDuration =~ 0 { 64 | targetState() 65 | } else { 66 | UIView.animate(withDuration: TimeInterval(animDuration), delay: TimeInterval(animDelay), options: .curveEaseOut, animations: { 67 | targetState() 68 | }, completion: nil) 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /SwiftCharts/Views/ChartPointTextCircleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPointTextCircleView.swift 3 | // swift_charts 4 | // 5 | // Created by ischuetz on 14/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class ChartPointTextCircleView: UILabel { 12 | 13 | fileprivate let targetCenter: CGPoint 14 | open var viewTapped: ((ChartPointTextCircleView) -> ())? 15 | 16 | open var selected: Bool = false { 17 | didSet { 18 | if selected { 19 | textColor = UIColor.white 20 | layer.borderColor = UIColor.white.cgColor 21 | layer.backgroundColor = UIColor.black.cgColor 22 | 23 | } else { 24 | textColor = UIColor.black 25 | layer.borderColor = UIColor.black.cgColor 26 | layer.backgroundColor = UIColor.white.cgColor 27 | } 28 | } 29 | } 30 | 31 | public init(chartPoint: ChartPoint, center: CGPoint, diameter: CGFloat, cornerRadius: CGFloat, borderWidth: CGFloat, font: UIFont) { 32 | 33 | targetCenter = center 34 | 35 | super.init(frame: CGRect(x: 0, y: center.y - diameter / 2, width: diameter, height: diameter)) 36 | 37 | self.textColor = UIColor.black 38 | self.text = chartPoint.description 39 | self.font = font 40 | self.layer.cornerRadius = cornerRadius 41 | self.layer.borderWidth = borderWidth 42 | self.textAlignment = NSTextAlignment.center 43 | self.layer.borderColor = UIColor.gray.cgColor 44 | 45 | let c = UIColor(red: 1, green: 1, blue: 1, alpha: 0.85) 46 | self.layer.backgroundColor = c.cgColor 47 | 48 | isUserInteractionEnabled = true 49 | } 50 | 51 | override open func didMoveToSuperview() { 52 | super.didMoveToSuperview() 53 | } 54 | 55 | required public init(coder aDecoder: NSCoder) { 56 | fatalError("init(coder:) has not been implemented") 57 | } 58 | 59 | override open func touchesEnded(_ touches: Set, with event: UIEvent?) { 60 | viewTapped?(self) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /SwiftCharts/Views/ChartPointViewBarGreyOut.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartPointViewBarGreyOut.swift 3 | // Examples 4 | // 5 | // Created by ischuetz on 15/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct ChartBarGreyOutSettings { 12 | let barSettings: ChartBarViewSettings = ChartBarViewSettings() 13 | let greyOutDelay: Float = 1 14 | let greyOutAnimDuration: Float = 0.5 15 | } 16 | 17 | open class ChartPointViewBarGreyOut: ChartPointViewBar { 18 | 19 | fileprivate let greyOutSettings: ChartBarGreyOutSettings 20 | 21 | init(p1: CGPoint, p2: CGPoint, width: CGFloat, bgColor: UIColor?, settings: ChartBarGreyOutSettings) { 22 | self.greyOutSettings = settings 23 | super.init(p1: p1, p2: p2, width: width, bgColor: bgColor, settings: settings.barSettings) 24 | } 25 | 26 | required public init(coder aDecoder: NSCoder) { 27 | fatalError("init(coder:) has not been implemented") 28 | } 29 | 30 | public required init(p1: CGPoint, p2: CGPoint, width: CGFloat, bgColor: UIColor?, settings: ChartBarViewSettings) { 31 | self.greyOutSettings = ChartBarGreyOutSettings() 32 | super.init(p1: p1, p2: p2, width: width, bgColor: bgColor, settings: settings) 33 | } 34 | 35 | override open func didMoveToSuperview() { 36 | super.didMoveToSuperview() 37 | 38 | UIView.animate(withDuration: CFTimeInterval(greyOutSettings.greyOutAnimDuration), delay: CFTimeInterval(greyOutSettings.greyOutDelay), options: UIView.AnimationOptions.curveEaseOut, animations: { 39 | self.backgroundColor = UIColor.gray 40 | }, completion: nil) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /SwiftCharts/Views/ChartViewAlphaAnimator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartViewAlphaAnimator.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 02/09/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct ChartViewAlphaAnimator: ChartViewAnimator { 12 | 13 | public let initAlpha: CGFloat 14 | public let targetAlpha: CGFloat 15 | 16 | public init(initAlpha: CGFloat, targetAlpha: CGFloat) { 17 | self.initAlpha = initAlpha 18 | self.targetAlpha = targetAlpha 19 | } 20 | 21 | public func initState(_ view: UIView) { 22 | view.alpha = initAlpha 23 | } 24 | 25 | public func targetState(_ view: UIView) { 26 | view.alpha = targetAlpha 27 | } 28 | } -------------------------------------------------------------------------------- /SwiftCharts/Views/ChartViewAnimator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartViewAnimator.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 02/09/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Animates a view from init state to target state and back. General animation settings like duration, delay, etc. are defined in the containing ChartViewAnimators instance. 12 | public protocol ChartViewAnimator { 13 | 14 | func initState(_ view: UIView) 15 | 16 | func targetState(_ view: UIView) 17 | 18 | 19 | func prepare(_ view: UIView) 20 | 21 | func animate(_ view: UIView) 22 | 23 | func invert(_ view: UIView) 24 | } 25 | 26 | extension ChartViewAnimator { 27 | 28 | public func prepare(_ view: UIView) { 29 | initState(view) 30 | } 31 | 32 | public func animate(_ view: UIView) { 33 | targetState(view) 34 | } 35 | 36 | public func invert(_ view: UIView) { 37 | initState(view) 38 | } 39 | } -------------------------------------------------------------------------------- /SwiftCharts/Views/ChartViewGrowAnimator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartViewGrowAnimator.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 02/09/16. 6 | // Copyright © 2016 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public struct ChartViewGrowAnimator: ChartViewAnimator { 12 | 13 | public let anchor: CGPoint 14 | 15 | public init(anchor: CGPoint) { 16 | self.anchor = anchor 17 | } 18 | 19 | public func prepare(_ view: UIView) { 20 | view.layer.anchorPoint = anchor 21 | let offsetX = view.frame.width * (0.5 - anchor.x) 22 | let offsetY = view.frame.height * (0.5 - anchor.y) 23 | view.frame = view.frame.offsetBy(dx: -offsetX, dy: -offsetY) 24 | 25 | initState(view) 26 | } 27 | 28 | public func initState(_ view: UIView) { 29 | view.transform = CGAffineTransform(scaleX: 0.001, y: 0.001) 30 | } 31 | 32 | public func targetState(_ view: UIView) { 33 | view.transform = CGAffineTransform(scaleX: 1, y: 1) 34 | } 35 | } -------------------------------------------------------------------------------- /SwiftCharts/Views/CubicLinePathGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CubicLinePathGenerator.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 28/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class CubicLinePathGenerator: ChartLinesViewPathGenerator { 12 | 13 | fileprivate let tension1: CGFloat 14 | fileprivate let tension2: CGFloat 15 | 16 | /** 17 | - parameter tension1: p1 tension, where 0 is straight line. A value higher than 0.3 is not recommended. 18 | - parameter tension2: p2 tension, where 0 is straight line. A value higher than 0.3 is not recommended. 19 | */ 20 | public init(tension1: CGFloat, tension2: CGFloat) { 21 | self.tension1 = tension1 22 | self.tension2 = tension2 23 | } 24 | 25 | // src: http://stackoverflow.com/a/29876400/930450 (modified) 26 | open func generatePath(points: [CGPoint], lineWidth: CGFloat) -> UIBezierPath { 27 | 28 | let path = UIBezierPath() 29 | 30 | guard !points.isEmpty else {return path} 31 | 32 | var p0: CGPoint 33 | var p1: CGPoint 34 | var p2: CGPoint 35 | var p3: CGPoint 36 | var tensionBezier1: CGFloat 37 | var tensionBezier2: CGFloat 38 | 39 | path.lineCapStyle = .round 40 | path.lineJoinStyle = .round 41 | 42 | var previousPoint1: CGPoint = CGPoint.zero 43 | 44 | path.move(to: points.first!) 45 | 46 | for i in 0..<(points.count - 1) { 47 | p1 = points[i] 48 | p2 = points[i + 1] 49 | 50 | tensionBezier1 = tension1 51 | tensionBezier2 = tension2 52 | 53 | if i > 0 { // Exception for first line because there is no previous point 54 | p0 = previousPoint1 55 | 56 | if p2.y - p1.y =~ p2.y - p0.y { 57 | tensionBezier1 = 0 58 | } 59 | 60 | } else { 61 | tensionBezier1 = 0 62 | p0 = p1 63 | } 64 | 65 | if i < points.count - 2 { // Exception for last line because there is no next point 66 | p3 = points[i + 2] 67 | if p3.y - p2.y =~ p2.y - p1.y { 68 | tensionBezier2 = 0 69 | } 70 | } else { 71 | p3 = p2 72 | tensionBezier2 = 0 73 | } 74 | 75 | let controlPoint1 = CGPoint(x: p1.x + (p2.x - p1.x) / 3, y: p1.y - (p1.y - p2.y) / 3 - (p0.y - p1.y) * tensionBezier1) 76 | let controlPoint2 = CGPoint(x: p1.x + 2 * (p2.x - p1.x) / 3, y: (p1.y - 2 * (p1.y - p2.y) / 3) + (p2.y - p3.y) * tensionBezier2) 77 | 78 | path.addCurve(to: p2, controlPoint1: controlPoint1, controlPoint2: controlPoint2) 79 | 80 | previousPoint1 = p1; 81 | } 82 | 83 | return path 84 | } 85 | 86 | open func generateAreaPath(points: [CGPoint], lineWidth: CGFloat) -> UIBezierPath { 87 | return generatePath(points: points, lineWidth: lineWidth) 88 | } 89 | } 90 | 91 | -------------------------------------------------------------------------------- /SwiftCharts/Views/HandlingLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HandlingLabel.swift 3 | // Examples 4 | // 5 | // Created by ischuetz on 18/05/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // Convenience view to handle events without subclassing 12 | open class HandlingLabel: UILabel { 13 | 14 | open var movedToSuperViewHandler: (() -> ())? 15 | open var touchHandler: (() -> ())? 16 | 17 | public override init(frame: CGRect) { 18 | super.init(frame: frame) 19 | sharedInit() 20 | } 21 | 22 | public required init?(coder aDecoder: NSCoder) { 23 | super.init(coder: aDecoder) 24 | sharedInit() 25 | } 26 | 27 | fileprivate func sharedInit() { 28 | isUserInteractionEnabled = true 29 | } 30 | 31 | override open func didMoveToSuperview() { 32 | movedToSuperViewHandler?() 33 | } 34 | 35 | override open func touchesEnded(_ touches: Set, with event: UIEvent?) { 36 | touchHandler?() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SwiftCharts/Views/HandlingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartItemView.swift 3 | // swift_charts 4 | // 5 | // Created by ischuetz on 15/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // Convenience view to handle events without subclassing 12 | open class HandlingView: UIView { 13 | 14 | open var movedToSuperViewHandler: (() -> ())? 15 | open var touchHandler: (() -> ())? 16 | 17 | override open func didMoveToSuperview() { 18 | movedToSuperViewHandler?() 19 | } 20 | 21 | override open func touchesEnded(_ touches: Set, with event: UIEvent?) { 22 | touchHandler?() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SwiftCharts/Views/StraightLinePathGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StraigthLinePathGenerator.swift 3 | // SwiftCharts 4 | // 5 | // Created by ischuetz on 28/04/15. 6 | // Copyright (c) 2015 ivanschuetz. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | open class StraightLinePathGenerator: ChartLinesViewPathGenerator { 12 | 13 | public init() {} 14 | 15 | open func generatePath(points: [CGPoint], lineWidth: CGFloat) -> UIBezierPath { 16 | 17 | let progressline = UIBezierPath() 18 | 19 | if points.count >= 2 { 20 | 21 | progressline.lineWidth = lineWidth 22 | progressline.lineCapStyle = .round 23 | progressline.lineJoinStyle = .round 24 | 25 | for i in 0..<(points.count - 1) { 26 | let p1 = points[i] 27 | let p2 = points[i + 1] 28 | 29 | progressline.move(to: p1) 30 | progressline.addLine(to: p2) 31 | } 32 | } 33 | 34 | return progressline 35 | } 36 | 37 | open func generateAreaPath(points: [CGPoint], lineWidth: CGFloat) -> UIBezierPath { 38 | let progressline = UIBezierPath() 39 | progressline.lineWidth = 1.0 40 | progressline.lineCapStyle = .round 41 | progressline.lineJoinStyle = .round 42 | 43 | if let p = points.first { 44 | progressline.move(to: p) 45 | } 46 | 47 | if points.count >= 2 { 48 | for i in 1..