├── .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..