From 0bf57aebdf2db0a3a4bb578abf1495a2f298c8d0 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Tue, 23 Feb 2016 18:48:49 +0200 Subject: [PATCH] New features for animating the viewport (Closes #318) --- Charts/Charts.xcodeproj/project.pbxproj | 45 ++- Charts/Classes/Animation/ChartAnimator.swift | 2 +- .../Classes/Charts/BarLineChartViewBase.swift | 343 +++++++++++++++--- Charts/Classes/Charts/ChartViewBase.swift | 24 +- Charts/Classes/Jobs/AnimatedJob.swift | 137 +++++++ Charts/Classes/Jobs/AnimatedMoveViewJob.swift | 54 +++ Charts/Classes/Jobs/AnimatedZoomViewJob.swift | 95 +++++ Charts/Classes/Jobs/MoveViewJob.swift | 47 +++ Charts/Classes/Jobs/ViewPortJob.swift | 42 +++ Charts/Classes/Jobs/ZoomViewJob.swift | 69 ++++ .../Classes/Utils/ChartViewPortHandler.swift | 40 ++ .../Classes/Demos/LineChart2ViewController.m | 5 + 12 files changed, 839 insertions(+), 64 deletions(-) create mode 100644 Charts/Classes/Jobs/AnimatedJob.swift create mode 100644 Charts/Classes/Jobs/AnimatedMoveViewJob.swift create mode 100644 Charts/Classes/Jobs/AnimatedZoomViewJob.swift create mode 100644 Charts/Classes/Jobs/MoveViewJob.swift create mode 100644 Charts/Classes/Jobs/ViewPortJob.swift create mode 100644 Charts/Classes/Jobs/ZoomViewJob.swift diff --git a/Charts/Charts.xcodeproj/project.pbxproj b/Charts/Charts.xcodeproj/project.pbxproj index 141d7b733a..49f5fb157d 100644 --- a/Charts/Charts.xcodeproj/project.pbxproj +++ b/Charts/Charts.xcodeproj/project.pbxproj @@ -88,6 +88,18 @@ 5BA8EC891A9D151C00CE82E1 /* ChartDataBaseFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC761A9D151C00CE82E1 /* ChartDataBaseFilter.swift */; }; 5BAAA8561BB08E1D00B20D4D /* CombinedHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAAA8551BB08E1D00B20D4D /* CombinedHighlighter.swift */; }; 5BB6EC1D1ACC28AB006E9C25 /* ChartTransformerHorizontalBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BB6EC1C1ACC28AB006E9C25 /* ChartTransformerHorizontalBarChart.swift */; }; + 5BBBD9E11C80976300D01235 /* ZoomViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBBD9E01C80976300D01235 /* ZoomViewJob.swift */; }; + 5BBBD9E21C80976300D01235 /* ZoomViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBBD9E01C80976300D01235 /* ZoomViewJob.swift */; }; + 5BCAA74A1C7CAA4E00F83F3B /* ViewPortJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA7491C7CAA4E00F83F3B /* ViewPortJob.swift */; }; + 5BCAA74B1C7CAA4E00F83F3B /* ViewPortJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA7491C7CAA4E00F83F3B /* ViewPortJob.swift */; }; + 5BCAA74D1C7CAB4900F83F3B /* MoveViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA74C1C7CAB4900F83F3B /* MoveViewJob.swift */; }; + 5BCAA74E1C7CAB4900F83F3B /* MoveViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA74C1C7CAB4900F83F3B /* MoveViewJob.swift */; }; + 5BCAA7501C7CAD4C00F83F3B /* AnimatedJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA74F1C7CAD4C00F83F3B /* AnimatedJob.swift */; }; + 5BCAA7511C7CAD4C00F83F3B /* AnimatedJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA74F1C7CAD4C00F83F3B /* AnimatedJob.swift */; }; + 5BCAA7531C7CB03900F83F3B /* AnimatedMoveViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA7521C7CB03900F83F3B /* AnimatedMoveViewJob.swift */; }; + 5BCAA7541C7CB03900F83F3B /* AnimatedMoveViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA7521C7CB03900F83F3B /* AnimatedMoveViewJob.swift */; }; + 5BCAA7561C7CB0DE00F83F3B /* AnimatedZoomViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA7551C7CB0DE00F83F3B /* AnimatedZoomViewJob.swift */; }; + 5BCAA7571C7CB0DE00F83F3B /* AnimatedZoomViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA7551C7CB0DE00F83F3B /* AnimatedZoomViewJob.swift */; }; 5BD4C57C1BCDBF6C00462351 /* BarChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD4C5751BCDBF6C00462351 /* BarChartDataProvider.swift */; }; 5BD4C57D1BCDBF6C00462351 /* BarLineScatterCandleBubbleChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD4C5761BCDBF6C00462351 /* BarLineScatterCandleBubbleChartDataProvider.swift */; }; 5BD4C57E1BCDBF6C00462351 /* BubbleChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD4C5771BCDBF6C00462351 /* BubbleChartDataProvider.swift */; }; @@ -320,6 +332,12 @@ 5BA8EC7C1A9D151C00CE82E1 /* ChartUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartUtils.swift; sourceTree = ""; }; 5BAAA8551BB08E1D00B20D4D /* CombinedHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombinedHighlighter.swift; sourceTree = ""; }; 5BB6EC1C1ACC28AB006E9C25 /* ChartTransformerHorizontalBarChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartTransformerHorizontalBarChart.swift; sourceTree = ""; }; + 5BBBD9E01C80976300D01235 /* ZoomViewJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZoomViewJob.swift; sourceTree = ""; }; + 5BCAA7491C7CAA4E00F83F3B /* ViewPortJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewPortJob.swift; sourceTree = ""; }; + 5BCAA74C1C7CAB4900F83F3B /* MoveViewJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoveViewJob.swift; sourceTree = ""; }; + 5BCAA74F1C7CAD4C00F83F3B /* AnimatedJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedJob.swift; sourceTree = ""; }; + 5BCAA7521C7CB03900F83F3B /* AnimatedMoveViewJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedMoveViewJob.swift; sourceTree = ""; }; + 5BCAA7551C7CB0DE00F83F3B /* AnimatedZoomViewJob.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnimatedZoomViewJob.swift; sourceTree = ""; }; 5BD4C5751BCDBF6C00462351 /* BarChartDataProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartDataProvider.swift; sourceTree = ""; }; 5BD4C5761BCDBF6C00462351 /* BarLineScatterCandleBubbleChartDataProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarLineScatterCandleBubbleChartDataProvider.swift; sourceTree = ""; }; 5BD4C5771BCDBF6C00462351 /* BubbleChartDataProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleChartDataProvider.swift; sourceTree = ""; }; @@ -515,6 +533,7 @@ 5B6654D41BB0A86F00890030 /* Formatters */, 5B0032431B65247600B6A2FE /* Highlight */, 5BD4C5661BCD9F0600462351 /* Interfaces */, + 5BCAA7481C7CAA0800F83F3B /* Jobs */, 5B759ED41A9F98A90039D97F /* Renderers */, 5BA8EC781A9D151C00CE82E1 /* Utils */, ); @@ -586,6 +605,19 @@ path = Utils; sourceTree = ""; }; + 5BCAA7481C7CAA0800F83F3B /* Jobs */ = { + isa = PBXGroup; + children = ( + 5BCAA7491C7CAA4E00F83F3B /* ViewPortJob.swift */, + 5BCAA74C1C7CAB4900F83F3B /* MoveViewJob.swift */, + 5BBBD9E01C80976300D01235 /* ZoomViewJob.swift */, + 5BCAA74F1C7CAD4C00F83F3B /* AnimatedJob.swift */, + 5BCAA7521C7CB03900F83F3B /* AnimatedMoveViewJob.swift */, + 5BCAA7551C7CB0DE00F83F3B /* AnimatedZoomViewJob.swift */, + ); + path = Jobs; + sourceTree = ""; + }; 5BD4C5661BCD9F0600462351 /* Interfaces */ = { isa = PBXGroup; children = ( @@ -816,6 +848,7 @@ buildActionMask = 2147483647; files = ( 5B680D1F1A9D17C30026A057 /* ChartAxisBase.swift in Sources */, + 5BCAA74D1C7CAB4900F83F3B /* MoveViewJob.swift in Sources */, 5BAAA8561BB08E1D00B20D4D /* CombinedHighlighter.swift in Sources */, 659400BC1BF463C1004F9C27 /* CombinedChartData.swift in Sources */, 5B680D211A9D17C30026A057 /* ChartLimitLine.swift in Sources */, @@ -830,6 +863,7 @@ 5B6A548B1AA66A3D000F57C2 /* LineChartRenderer.swift in Sources */, 5B6A54821AA5DF34000F57C2 /* ChartYAxisRendererRadarChart.swift in Sources */, 659400B81BF463C1004F9C27 /* ChartDataEntry.swift in Sources */, + 5BCAA74A1C7CAA4E00F83F3B /* ViewPortJob.swift in Sources */, 5BD4C5821BCDBF6C00462351 /* ScatterChartDataProvider.swift in Sources */, 5B6A54931AA66AAB000F57C2 /* CombinedChartRenderer.swift in Sources */, 659400C01BF463C1004F9C27 /* LineChartDataSet.swift in Sources */, @@ -840,6 +874,7 @@ 5B680D221A9D17C30026A057 /* ChartXAxis.swift in Sources */, 5BA8EC891A9D151C00CE82E1 /* ChartDataBaseFilter.swift in Sources */, 659400A21BF463C1004F9C27 /* BarChartDataEntry.swift in Sources */, + 5BCAA7501C7CAD4C00F83F3B /* AnimatedJob.swift in Sources */, 5B6654DB1BB0A86F00890030 /* ChartXAxisValueFormatter.swift in Sources */, 5B6A54A31AA66B7C000F57C2 /* LineChartView.swift in Sources */, 5B6A54891AA66A1A000F57C2 /* PieChartRenderer.swift in Sources */, @@ -867,6 +902,7 @@ 5BB6EC1D1ACC28AB006E9C25 /* ChartTransformerHorizontalBarChart.swift in Sources */, 5B680D271A9D17C30026A057 /* ChartColorTemplates.swift in Sources */, 659400C21BF463C1004F9C27 /* LineRadarChartDataSet.swift in Sources */, + 5BBBD9E11C80976300D01235 /* ZoomViewJob.swift in Sources */, 5B6A54951AA66AC0000F57C2 /* CandleStickChartRenderer.swift in Sources */, 5B680D291A9D17C30026A057 /* ChartSelectionDetail.swift in Sources */, 659400A01BF463C1004F9C27 /* BarChartData.swift in Sources */, @@ -886,12 +922,12 @@ 5BD8F06D1AB897D500566E05 /* ChartViewPortHandler.swift in Sources */, 5B8FE2B11B68FFF900910C9E /* LineScatterCandleRadarChartRenderer.swift in Sources */, 5B4AC1CD1C58F55B0028D1A6 /* LineRadarChartRenderer.swift in Sources */, + 5BCAA7531C7CB03900F83F3B /* AnimatedMoveViewJob.swift in Sources */, 65F06F9E1BE814800074498D /* IScatterChartDataSet.swift in Sources */, 5BD4C57E1BCDBF6C00462351 /* BubbleChartDataProvider.swift in Sources */, 5B6A54971AA66AD2000F57C2 /* BarChartRenderer.swift in Sources */, 659400CA1BF463C2004F9C27 /* RadarChartData.swift in Sources */, 5B6A546B1AA5C23F000F57C2 /* ChartMarker.swift in Sources */, - 5B88D8BB1C6352C400B54CBD /* ChartDefaultFillFormatter.swift in Sources */, 5BD4C5801BCDBF6C00462351 /* ChartDataProvider.swift in Sources */, 5BD4C57C1BCDBF6C00462351 /* BarChartDataProvider.swift in Sources */, 5B0032491B6525FC00B6A2FE /* ChartHighlighter.swift in Sources */, @@ -920,6 +956,7 @@ 5B0032451B6524AD00B6A2FE /* ChartHighlight.swift in Sources */, 65F06F8E1BE807040074498D /* IChartDataSet.swift in Sources */, 5B680D201A9D17C30026A057 /* ChartLegend.swift in Sources */, + 5BCAA7561C7CB0DE00F83F3B /* AnimatedZoomViewJob.swift in Sources */, 5B0032471B6524D300B6A2FE /* ChartRange.swift in Sources */, 5BD8F06E1AB89AD800566E05 /* HorizontalBarChartView.swift in Sources */, ); @@ -930,6 +967,7 @@ buildActionMask = 2147483647; files = ( 5597263C1BDABC0500E05E59 /* ChartDefaultXAxisValueFormatter.swift in Sources */, + 5BCAA74E1C7CAB4900F83F3B /* MoveViewJob.swift in Sources */, 5597263D1BDABC0500E05E59 /* ChartFillFormatter.swift in Sources */, 659400BD1BF463C1004F9C27 /* CombinedChartData.swift in Sources */, 5597263E1BDABC0500E05E59 /* ChartXAxisValueFormatter.swift in Sources */, @@ -944,6 +982,7 @@ 559726431BDABC0500E05E59 /* CandleChartDataProvider.swift in Sources */, 559726441BDABC0500E05E59 /* ChartDataProvider.swift in Sources */, 659400B91BF463C1004F9C27 /* ChartDataEntry.swift in Sources */, + 5BCAA74B1C7CAA4E00F83F3B /* ViewPortJob.swift in Sources */, 559726451BDABC0500E05E59 /* LineChartDataProvider.swift in Sources */, 559726461BDABC0500E05E59 /* ScatterChartDataProvider.swift in Sources */, 659400C11BF463C1004F9C27 /* LineChartDataSet.swift in Sources */, @@ -954,6 +993,7 @@ A52C5C3F1BAC5D1100594CDD /* ChartAnimator.swift in Sources */, A52C5C401BAC5D1100594CDD /* ChartAnimationEasing.swift in Sources */, 659400A31BF463C1004F9C27 /* BarChartDataEntry.swift in Sources */, + 5BCAA7511C7CAD4C00F83F3B /* AnimatedJob.swift in Sources */, A52C5C411BAC5D1100594CDD /* BarChartView.swift in Sources */, A52C5C421BAC5D1100594CDD /* BarLineChartViewBase.swift in Sources */, A52C5C431BAC5D1100594CDD /* BubbleChartView.swift in Sources */, @@ -981,6 +1021,7 @@ 65F06F951BE812210074498D /* IBarLineScatterCandleBubbleChartDataSet.swift in Sources */, A52C5C511BAC5D1100594CDD /* ChartMarker.swift in Sources */, 659400C31BF463C1004F9C27 /* LineRadarChartDataSet.swift in Sources */, + 5BBBD9E21C80976300D01235 /* ZoomViewJob.swift in Sources */, A52C5C521BAC5D1100594CDD /* ChartXAxis.swift in Sources */, 5B4AC1CB1C58A2B90028D1A6 /* ChartFill.swift in Sources */, A52C5C531BAC5D1100594CDD /* ChartYAxis.swift in Sources */, @@ -1000,6 +1041,7 @@ A52C5C761BAC5D1100594CDD /* BubbleChartRenderer.swift in Sources */, A52C5C771BAC5D1100594CDD /* CandleStickChartRenderer.swift in Sources */, 65F06F9F1BE814800074498D /* IScatterChartDataSet.swift in Sources */, + 5BCAA7541C7CB03900F83F3B /* AnimatedMoveViewJob.swift in Sources */, A52C5C781BAC5D1100594CDD /* CombinedChartRenderer.swift in Sources */, A52C5C791BAC5D1100594CDD /* ChartDataRendererBase.swift in Sources */, A52C5C7A1BAC5D1200594CDD /* HorizontalBarChartRenderer.swift in Sources */, @@ -1033,6 +1075,7 @@ 65F06F8F1BE807040074498D /* IChartDataSet.swift in Sources */, A52C5C8C1BAC5D1200594CDD /* ChartTransformer.swift in Sources */, A52C5C8D1BAC5D1200594CDD /* ChartTransformerHorizontalBarChart.swift in Sources */, + 5BCAA7571C7CB0DE00F83F3B /* AnimatedZoomViewJob.swift in Sources */, A52C5C8E1BAC5D1200594CDD /* ChartUtils.swift in Sources */, A52C5C8F1BAC5D1200594CDD /* ChartViewPortHandler.swift in Sources */, ); diff --git a/Charts/Classes/Animation/ChartAnimator.swift b/Charts/Classes/Animation/ChartAnimator.swift index 23fc5a7aec..bbab65322b 100644 --- a/Charts/Classes/Animation/ChartAnimator.swift +++ b/Charts/Classes/Animation/ChartAnimator.swift @@ -30,7 +30,7 @@ public class ChartAnimator: NSObject public var updateBlock: (() -> Void)? public var stopBlock: (() -> Void)? - /// the phase that is animated and influences the drawn values on the y-axis + /// the phase that is animated and influences the drawn values on the x-axis public var phaseX: CGFloat = 1.0 /// the phase that is animated and influences the drawn values on the y-axis diff --git a/Charts/Classes/Charts/BarLineChartViewBase.swift b/Charts/Classes/Charts/BarLineChartViewBase.swift index 574fcf8f79..b44de7e891 100644 --- a/Charts/Classes/Charts/BarLineChartViewBase.swift +++ b/Charts/Classes/Charts/BarLineChartViewBase.swift @@ -1029,7 +1029,109 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar calculateOffsets() setNeedsDisplay() } - + + /// Zooms in or out by the given scale factor. + /// x and y are the values which to zoom to or from (the values of the zoom center). + /// + /// - parameter scaleX: if < 1 --> zoom out, if > 1 --> zoom in + /// - parameter scaleY: if < 1 --> zoom out, if > 1 --> zoom in + /// - parameter xIndex: + /// - parameter yValue: + /// - parameter axis: + public func zoom( + scaleX: CGFloat, + scaleY: CGFloat, + xIndex: CGFloat, + yValue: Double, + axis: ChartYAxis.AxisDependency) + { + let job = ZoomViewJob(viewPortHandler: viewPortHandler, scaleX: scaleX, scaleY: scaleY, xIndex: xIndex, yValue: yValue, transformer: getTransformer(axis), axis: axis, view: self) + addViewportJob(job) + } + + /// Zooms by the specified scale factor to the specified values on the specified axis. + /// + /// - parameter scaleX: + /// - parameter scaleY: + /// - parameter xIndex: + /// - parameter yValue: + /// - parameter axis: which axis should be used as a reference for the y-axis + /// - parameter duration: the duration of the animation in seconds + /// - parameter easing: + public func zoomAndCenterViewAnimated( + scaleX scaleX: CGFloat, + scaleY: CGFloat, + xIndex: CGFloat, + yValue: Double, + axis: ChartYAxis.AxisDependency, + duration: NSTimeInterval, + easing: ChartEasingFunctionBlock?) + { + let origin = getValueByTouchPoint( + pt: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop), + axis: axis) + + let job = AnimatedZoomViewJob( + viewPortHandler: viewPortHandler, + transformer: getTransformer(axis), + view: self, + yAxis: getAxis(axis), + xValCount: _xAxis.values.count, + scaleX: scaleX, + scaleY: scaleY, + xOrigin: viewPortHandler.scaleX, + yOrigin: viewPortHandler.scaleY, + zoomCenterX: xIndex, + zoomCenterY: CGFloat(yValue), + zoomOriginX: origin.x, + zoomOriginY: origin.y, + duration: duration, + easing: easing) + + addViewportJob(job) + } + + /// Zooms by the specified scale factor to the specified values on the specified axis. + /// + /// - parameter scaleX: + /// - parameter scaleY: + /// - parameter xIndex: + /// - parameter yValue: + /// - parameter axis: which axis should be used as a reference for the y-axis + /// - parameter duration: the duration of the animation in seconds + /// - parameter easing: + public func zoomAndCenterViewAnimated( + scaleX scaleX: CGFloat, + scaleY: CGFloat, + xIndex: CGFloat, + yValue: Double, + axis: ChartYAxis.AxisDependency, + duration: NSTimeInterval, + easingOption: ChartEasingOption) + { + zoomAndCenterViewAnimated(scaleX: scaleX, scaleY: scaleY, xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) + } + + /// Zooms by the specified scale factor to the specified values on the specified axis. + /// + /// - parameter scaleX: + /// - parameter scaleY: + /// - parameter xIndex: + /// - parameter yValue: + /// - parameter axis: which axis should be used as a reference for the y-axis + /// - parameter duration: the duration of the animation in seconds + /// - parameter easing: + public func zoomAndCenterViewAnimated( + scaleX scaleX: CGFloat, + scaleY: CGFloat, + xIndex: CGFloat, + yValue: Double, + axis: ChartYAxis.AxisDependency, + duration: NSTimeInterval) + { + zoomAndCenterViewAnimated(scaleX: scaleX, scaleY: scaleY, xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easingOption: .EaseInOutSine) + } + /// Resets all zooming and dragging and makes the chart fit exactly it's bounds. public func fitScreen() { @@ -1086,19 +1188,16 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// Moves the left side of the current viewport to the specified x-index. /// This also refreshes the chart by calling setNeedsDisplay(). - public func moveViewToX(xIndex: Int) + public func moveViewToX(xIndex: CGFloat) { - if (_viewPortHandler.hasChartDimens) - { - var pt = CGPoint(x: CGFloat(xIndex), y: 0.0) - - getTransformer(.Left).pointValueToPixel(&pt) - _viewPortHandler.centerViewPort(pt: pt, chart: self) - } - else - { - _sizeChangeEventActions.append({[weak self] () in self?.moveViewToX(xIndex); }) - } + let job = MoveViewJob( + viewPortHandler: viewPortHandler, + xIndex: xIndex, + yValue: 0.0, + transformer: getTransformer(.Left), + view: self) + + addViewportJob(job) } /// Centers the viewport to the specified y-value on the y-axis. @@ -1106,21 +1205,18 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// /// - parameter yValue: /// - parameter axis: - which axis should be used as a reference for the y-axis - public func moveViewToY(yValue: CGFloat, axis: ChartYAxis.AxisDependency) + public func moveViewToY(yValue: Double, axis: ChartYAxis.AxisDependency) { - if (_viewPortHandler.hasChartDimens) - { - let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY - - var pt = CGPoint(x: 0.0, y: yValue + valsInView / 2.0) - - getTransformer(axis).pointValueToPixel(&pt) - _viewPortHandler.centerViewPort(pt: pt, chart: self) - } - else - { - _sizeChangeEventActions.append({[weak self] () in self?.moveViewToY(yValue, axis: axis); }) - } + let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY + + let job = MoveViewJob( + viewPortHandler: viewPortHandler, + xIndex: 0, + yValue: yValue + Double(valsInView) / 2.0, + transformer: getTransformer(axis), + view: self) + + addViewportJob(job) } /// This will move the left side of the current viewport to the specified x-index on the x-axis, and center the viewport to the specified y-value on the y-axis. @@ -1129,21 +1225,88 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// - parameter xIndex: /// - parameter yValue: /// - parameter axis: - which axis should be used as a reference for the y-axis - public func moveViewTo(xIndex xIndex: Int, yValue: CGFloat, axis: ChartYAxis.AxisDependency) + public func moveViewTo(xIndex xIndex: CGFloat, yValue: Double, axis: ChartYAxis.AxisDependency) { - if (_viewPortHandler.hasChartDimens) - { - let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY - - var pt = CGPoint(x: CGFloat(xIndex), y: yValue + valsInView / 2.0) - - getTransformer(axis).pointValueToPixel(&pt) - _viewPortHandler.centerViewPort(pt: pt, chart: self) - } - else - { - _sizeChangeEventActions.append({[weak self] () in self?.moveViewTo(xIndex: xIndex, yValue: yValue, axis: axis); }) - } + let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY + + let job = MoveViewJob( + viewPortHandler: viewPortHandler, + xIndex: xIndex, + yValue: yValue + Double(valsInView) / 2.0, + transformer: getTransformer(axis), + view: self) + + addViewportJob(job) + } + + /// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated. + /// This also refreshes the chart by calling setNeedsDisplay(). + /// + /// - parameter xIndex: + /// - parameter yValue: + /// - parameter axis: which axis should be used as a reference for the y-axis + /// - parameter duration: the duration of the animation in seconds + /// - parameter easing: + public func moveViewToAnimated( + xIndex xIndex: CGFloat, + yValue: Double, + axis: ChartYAxis.AxisDependency, + duration: NSTimeInterval, + easing: ChartEasingFunctionBlock?) + { + let bounds = getValueByTouchPoint( + pt: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop), + axis: axis) + + let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY + + let job = AnimatedMoveViewJob( + viewPortHandler: viewPortHandler, + xIndex: xIndex, + yValue: yValue + Double(valsInView) / 2.0, + transformer: getTransformer(axis), + view: self, + xOrigin: bounds.x, + yOrigin: bounds.y, + duration: duration, + easing: easing) + + addViewportJob(job) + } + + /// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated. + /// This also refreshes the chart by calling setNeedsDisplay(). + /// + /// - parameter xIndex: + /// - parameter yValue: + /// - parameter axis: which axis should be used as a reference for the y-axis + /// - parameter duration: the duration of the animation in seconds + /// - parameter easing: + public func moveViewToAnimated( + xIndex xIndex: CGFloat, + yValue: Double, + axis: ChartYAxis.AxisDependency, + duration: NSTimeInterval, + easingOption: ChartEasingOption) + { + moveViewToAnimated(xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) + } + + /// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated. + /// This also refreshes the chart by calling setNeedsDisplay(). + /// + /// - parameter xIndex: + /// - parameter yValue: + /// - parameter axis: which axis should be used as a reference for the y-axis + /// - parameter duration: the duration of the animation in seconds + /// - parameter easing: + public func moveViewToAnimated( + xIndex xIndex: CGFloat, + yValue: Double, + axis: ChartYAxis.AxisDependency, + duration: NSTimeInterval) + { + moveViewToAnimated(xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easingOption: .EaseInOutSine) } /// This will move the center of the current viewport to the specified x-index and y-value. @@ -1152,22 +1315,90 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// - parameter xIndex: /// - parameter yValue: /// - parameter axis: - which axis should be used as a reference for the y-axis - public func centerViewTo(xIndex xIndex: Int, yValue: CGFloat, axis: ChartYAxis.AxisDependency) + public func centerViewTo( + xIndex xIndex: CGFloat, + yValue: Double, + axis: ChartYAxis.AxisDependency) { - if (_viewPortHandler.hasChartDimens) - { - let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY - let xsInView = CGFloat(xAxis.values.count) / _viewPortHandler.scaleX - - var pt = CGPoint(x: CGFloat(xIndex) - xsInView / 2.0, y: yValue + valsInView / 2.0) - - getTransformer(axis).pointValueToPixel(&pt) - _viewPortHandler.centerViewPort(pt: pt, chart: self) - } - else - { - _sizeChangeEventActions.append({[weak self] () in self?.centerViewTo(xIndex: xIndex, yValue: yValue, axis: axis); }) - } + let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY + let xsInView = CGFloat(xAxis.values.count) / _viewPortHandler.scaleX + + let job = MoveViewJob( + viewPortHandler: viewPortHandler, + xIndex: xIndex - xsInView / 2.0, + yValue: yValue + Double(valsInView) / 2.0, + transformer: getTransformer(axis), + view: self) + + addViewportJob(job) + } + + /// This will move the center of the current viewport to the specified x-value and y-value animated. + /// + /// - parameter xIndex: + /// - parameter yValue: + /// - parameter axis: which axis should be used as a reference for the y-axis + /// - parameter duration: the duration of the animation in seconds + /// - parameter easing: + public func centerViewToAnimated( + xIndex xIndex: CGFloat, + yValue: Double, + axis: ChartYAxis.AxisDependency, + duration: NSTimeInterval, + easing: ChartEasingFunctionBlock?) + { + let bounds = getValueByTouchPoint( + pt: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop), + axis: axis) + + let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY + let xsInView = CGFloat(xAxis.values.count) / _viewPortHandler.scaleX + + let job = AnimatedMoveViewJob( + viewPortHandler: viewPortHandler, + xIndex: xIndex - xsInView / 2.0, + yValue: yValue + Double(valsInView) / 2.0, + transformer: getTransformer(axis), + view: self, + xOrigin: bounds.x, + yOrigin: bounds.y, + duration: duration, + easing: easing) + + addViewportJob(job) + } + + /// This will move the center of the current viewport to the specified x-value and y-value animated. + /// + /// - parameter xIndex: + /// - parameter yValue: + /// - parameter axis: which axis should be used as a reference for the y-axis + /// - parameter duration: the duration of the animation in seconds + /// - parameter easing: + public func centerViewToAnimated( + xIndex xIndex: CGFloat, + yValue: Double, + axis: ChartYAxis.AxisDependency, + duration: NSTimeInterval, + easingOption: ChartEasingOption) + { + centerViewToAnimated(xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) + } + + /// This will move the center of the current viewport to the specified x-value and y-value animated. + /// + /// - parameter xIndex: + /// - parameter yValue: + /// - parameter axis: which axis should be used as a reference for the y-axis + /// - parameter duration: the duration of the animation in seconds + /// - parameter easing: + public func centerViewToAnimated( + xIndex xIndex: CGFloat, + yValue: Double, + axis: ChartYAxis.AxisDependency, + duration: NSTimeInterval) + { + centerViewToAnimated(xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easingOption: .EaseInOutSine) } /// Sets custom offsets for the current `ChartViewPort` (the offsets on the sides of the actual chart window). Setting this will prevent the chart from automatically calculating it's offsets. Use `resetViewPortOffsets()` to undo this. diff --git a/Charts/Classes/Charts/ChartViewBase.swift b/Charts/Classes/Charts/ChartViewBase.swift index b83fc8f6a0..46e0a8a030 100755 --- a/Charts/Classes/Charts/ChartViewBase.swift +++ b/Charts/Classes/Charts/ChartViewBase.swift @@ -861,8 +861,7 @@ public class ChartViewBase: UIView, ChartDataProvider, ChartAnimatorDelegate } #endif - internal typealias VoidClosureType = () -> () - internal var _sizeChangeEventActions = [VoidClosureType]() + internal var _viewportJobs = [ViewPortJob]() public override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer) { @@ -877,9 +876,10 @@ public class ChartViewBase: UIView, ChartDataProvider, ChartAnimatorDelegate _viewPortHandler.setChartDimens(width: bounds.size.width, height: bounds.size.height) // Finish any pending viewport changes - while (!_sizeChangeEventActions.isEmpty) + while (!_viewportJobs.isEmpty) { - _sizeChangeEventActions.removeAtIndex(0)() + let job = _viewportJobs.removeAtIndex(0) + job.doJob() } notifyDataSetChanged() @@ -887,9 +887,21 @@ public class ChartViewBase: UIView, ChartDataProvider, ChartAnimatorDelegate } } - public func clearPendingViewPortChanges() + public func clearAllViewportJobs() { - _sizeChangeEventActions.removeAll(keepCapacity: false) + _viewportJobs.removeAll(keepCapacity: false) + } + + internal func addViewportJob(job: ViewPortJob) + { + if (_viewPortHandler.hasChartDimens) + { + job.doJob() + } + else + { + _viewportJobs.append(job) + } } /// **default**: true diff --git a/Charts/Classes/Jobs/AnimatedJob.swift b/Charts/Classes/Jobs/AnimatedJob.swift new file mode 100644 index 0000000000..5a903e79c9 --- /dev/null +++ b/Charts/Classes/Jobs/AnimatedJob.swift @@ -0,0 +1,137 @@ +// +// AnimatedJob.swift +// Charts +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +internal class AnimatedJob: ViewPortJob +{ + internal var phase: CGFloat = 1.0 + internal var xOrigin: CGFloat = 0.0 + internal var yOrigin: CGFloat = 0.0 + + private var _startTime: NSTimeInterval = 0.0 + private var _displayLink: CADisplayLink! + private var _duration: NSTimeInterval = 0.0 + private var _endTime: NSTimeInterval = 0.0 + + private var _easing: ChartEasingFunctionBlock? + + internal init( + viewPortHandler: ChartViewPortHandler, + xIndex: CGFloat, + yValue: Double, + transformer: ChartTransformer, + view: ChartViewBase, + xOrigin: CGFloat, + yOrigin: CGFloat, + duration: NSTimeInterval, + easing: ChartEasingFunctionBlock?) + { + super.init(viewPortHandler: viewPortHandler, + xIndex: xIndex, + yValue: yValue, + transformer: transformer, + view: view) + + self.xOrigin = xOrigin + self.yOrigin = yOrigin + self._duration = duration + self._easing = easing + } + + deinit + { + stop(finish: false) + } + + internal override func doJob() + { + start() + } + + private func start() + { + _startTime = CACurrentMediaTime() + _endTime = _startTime + _duration + _endTime = _endTime > _endTime ? _endTime : _endTime + + updateAnimationPhase(_startTime) + + _displayLink = CADisplayLink(target: self, selector: Selector("animationLoop")) + _displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes) + } + + private func stop(finish finish: Bool) + { + if (_displayLink != nil) + { + _displayLink.removeFromRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes) + _displayLink = nil + + if finish + { + if phase != 1.0 + { + phase = 1.0 + phase = 1.0 + + animationUpdate() + } + + animationEnd() + } + } + } + + private func updateAnimationPhase(currentTime: NSTimeInterval) + { + let elapsedTime: NSTimeInterval = currentTime - _startTime + let duration: NSTimeInterval = _duration + var elapsed: NSTimeInterval = elapsedTime + if elapsed > duration + { + elapsed = duration + } + + if _easing != nil + { + phase = _easing!(elapsed: elapsed, duration: duration) + } + else + { + phase = CGFloat(elapsed / duration) + } + } + + @objc private func animationLoop() + { + let currentTime: NSTimeInterval = CACurrentMediaTime() + + updateAnimationPhase(currentTime) + + animationUpdate() + + if (currentTime >= _endTime) + { + stop(finish: true) + } + } + + internal func animationUpdate() + { + // Override this + } + + internal func animationEnd() + { + // Override this + } +} \ No newline at end of file diff --git a/Charts/Classes/Jobs/AnimatedMoveViewJob.swift b/Charts/Classes/Jobs/AnimatedMoveViewJob.swift new file mode 100644 index 0000000000..96db598a04 --- /dev/null +++ b/Charts/Classes/Jobs/AnimatedMoveViewJob.swift @@ -0,0 +1,54 @@ +// +// AnimatedMoveViewJob.swift +// Charts +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +internal class AnimatedMoveViewJob: AnimatedJob +{ + internal override init( + viewPortHandler: ChartViewPortHandler, + xIndex: CGFloat, + yValue: Double, + transformer: ChartTransformer, + view: ChartViewBase, + xOrigin: CGFloat, + yOrigin: CGFloat, + duration: NSTimeInterval, + easing: ChartEasingFunctionBlock?) + { + super.init(viewPortHandler: viewPortHandler, + xIndex: xIndex, + yValue: yValue, + transformer: transformer, + view: view, + xOrigin: xOrigin, + yOrigin: yOrigin, + duration: duration, + easing: easing) + } + + internal override func animationUpdate() + { + guard let + viewPortHandler = viewPortHandler, + transformer = transformer, + view = view + else { return } + + var pt = CGPoint( + x: xOrigin + (xIndex - xOrigin) * phase, + y: yOrigin + (CGFloat(yValue) - yOrigin) * phase + ); + + transformer.pointValueToPixel(&pt) + viewPortHandler.centerViewPort(pt: pt, chart: view) + } +} \ No newline at end of file diff --git a/Charts/Classes/Jobs/AnimatedZoomViewJob.swift b/Charts/Classes/Jobs/AnimatedZoomViewJob.swift new file mode 100644 index 0000000000..619a3c32b4 --- /dev/null +++ b/Charts/Classes/Jobs/AnimatedZoomViewJob.swift @@ -0,0 +1,95 @@ +// +// AnimatedZoomViewJob.swift +// Charts +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +internal class AnimatedZoomViewJob: AnimatedJob +{ + internal var yAxis: ChartYAxis? + internal var xValCount: Int = 0 + internal var scaleX: CGFloat = 0.0 + internal var scaleY: CGFloat = 0.0 + internal var zoomOriginX: CGFloat = 0.0 + internal var zoomOriginY: CGFloat = 0.0 + internal var zoomCenterX: CGFloat = 0.0 + internal var zoomCenterY: CGFloat = 0.0 + + internal init( + viewPortHandler: ChartViewPortHandler, + transformer: ChartTransformer, + view: ChartViewBase, + yAxis: ChartYAxis, + xValCount: Int, + scaleX: CGFloat, + scaleY: CGFloat, + xOrigin: CGFloat, + yOrigin: CGFloat, + zoomCenterX: CGFloat, + zoomCenterY: CGFloat, + zoomOriginX: CGFloat, + zoomOriginY: CGFloat, + duration: NSTimeInterval, + easing: ChartEasingFunctionBlock?) + { + super.init(viewPortHandler: viewPortHandler, + xIndex: 0.0, + yValue: 0.0, + transformer: transformer, + view: view, + xOrigin: xOrigin, + yOrigin: yOrigin, + duration: duration, + easing: easing) + + self.yAxis = yAxis + self.xValCount = xValCount + self.scaleX = scaleX + self.scaleY = scaleY + self.zoomCenterX = zoomCenterX + self.zoomCenterY = zoomCenterY + self.zoomOriginX = zoomOriginX + self.zoomOriginY = zoomOriginY + } + + internal override func animationUpdate() + { + guard let + viewPortHandler = viewPortHandler, + transformer = transformer, + view = view + else { return } + + let scaleX = xOrigin + (self.scaleX - xOrigin) * phase + let scaleY = yOrigin + (self.scaleY - yOrigin) * phase + + var matrix = viewPortHandler.setZoom(scaleX: scaleX, scaleY: scaleY) + viewPortHandler.refresh(newMatrix: matrix, chart: view, invalidate: false) + + let valsInView = CGFloat(yAxis?.axisRange ?? 0.0) / viewPortHandler.scaleY + let xsInView = CGFloat(xValCount) / viewPortHandler.scaleX + + var pt = CGPoint( + x: zoomOriginX + ((zoomCenterX - xsInView / 2.0) - zoomOriginX) * phase, + y: zoomOriginY + ((zoomCenterY + valsInView / 2.0) - zoomOriginY) * phase + ) + + transformer.pointValueToPixel(&pt) + + matrix = viewPortHandler.translate(pt: pt) + viewPortHandler.refresh(newMatrix: matrix, chart: view, invalidate: true) + } + + internal override func animationEnd() + { + (view as? BarLineChartViewBase)?.calculateOffsets() + view?.setNeedsDisplay() + } +} diff --git a/Charts/Classes/Jobs/MoveViewJob.swift b/Charts/Classes/Jobs/MoveViewJob.swift new file mode 100644 index 0000000000..ec3c8e8906 --- /dev/null +++ b/Charts/Classes/Jobs/MoveViewJob.swift @@ -0,0 +1,47 @@ +// +// MoveViewJob.swift +// Charts +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +internal class MoveViewJob: ViewPortJob +{ + internal override init( + viewPortHandler: ChartViewPortHandler, + xIndex: CGFloat, + yValue: Double, + transformer: ChartTransformer, + view: ChartViewBase) + { + super.init( + viewPortHandler: viewPortHandler, + xIndex: xIndex, + yValue: yValue, + transformer: transformer, + view: view) + } + + internal override func doJob() + { + guard let + viewPortHandler = viewPortHandler, + transformer = transformer, + view = view + else { return } + + var pt = CGPoint( + x: xIndex, + y: CGFloat(yValue) + ); + + transformer.pointValueToPixel(&pt) + viewPortHandler.centerViewPort(pt: pt, chart: view) + } +} \ No newline at end of file diff --git a/Charts/Classes/Jobs/ViewPortJob.swift b/Charts/Classes/Jobs/ViewPortJob.swift new file mode 100644 index 0000000000..94058d8e43 --- /dev/null +++ b/Charts/Classes/Jobs/ViewPortJob.swift @@ -0,0 +1,42 @@ +// +// ViewPortJob.swift +// Charts +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +// This defines a viewport modification job, used for delaying or animating viewport changes +internal class ViewPortJob +{ + internal var point: CGPoint = CGPoint() + internal weak var viewPortHandler: ChartViewPortHandler? + internal var xIndex: CGFloat = 0.0 + internal var yValue: Double = 0.0 + internal weak var transformer: ChartTransformer? + internal weak var view: ChartViewBase? + + internal init( + viewPortHandler: ChartViewPortHandler, + xIndex: CGFloat, + yValue: Double, + transformer: ChartTransformer, + view: ChartViewBase) + { + self.viewPortHandler = viewPortHandler + self.xIndex = xIndex + self.yValue = yValue + self.transformer = transformer + self.view = view + } + + internal func doJob() + { + // Override this + } +} \ No newline at end of file diff --git a/Charts/Classes/Jobs/ZoomViewJob.swift b/Charts/Classes/Jobs/ZoomViewJob.swift new file mode 100644 index 0000000000..5edfff34a2 --- /dev/null +++ b/Charts/Classes/Jobs/ZoomViewJob.swift @@ -0,0 +1,69 @@ +// +// ZoomViewJob.swift +// Charts +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/ios-charts +// + +import Foundation + +internal class ZoomViewJob: ViewPortJob +{ + internal var scaleX: CGFloat = 0.0 + internal var scaleY: CGFloat = 0.0 + internal var axisDependency: ChartYAxis.AxisDependency = ChartYAxis.AxisDependency.Left + + internal init( + viewPortHandler: ChartViewPortHandler, + scaleX: CGFloat, + scaleY: CGFloat, + xIndex: CGFloat, + yValue: Double, + transformer: ChartTransformer, + axis: ChartYAxis.AxisDependency, + view: ChartViewBase) + { + super.init( + viewPortHandler: viewPortHandler, + xIndex: xIndex, + yValue: yValue, + transformer: transformer, + view: view) + + self.scaleX = scaleX + self.scaleY = scaleY + self.axisDependency = axis + } + + internal override func doJob() + { + guard let + viewPortHandler = viewPortHandler, + transformer = transformer, + view = view + else { return } + + var matrix = viewPortHandler.setZoom(scaleX: scaleX, scaleY: scaleY) + viewPortHandler.refresh(newMatrix: matrix, chart: view, invalidate: false) + + let valsInView = (view as! BarLineChartViewBase).getDeltaY(axisDependency) / viewPortHandler.scaleY + let xsInView = CGFloat((view as! BarLineChartViewBase).xAxis.values.count) / viewPortHandler.scaleX + + var pt = CGPoint( + x: xIndex - xsInView / 2.0, + y: CGFloat(yValue) + valsInView / 2.0 + ) + + transformer.pointValueToPixel(&pt) + + matrix = viewPortHandler.translate(pt: pt) + viewPortHandler.refresh(newMatrix: matrix, chart: view, invalidate: false) + + (view as! BarLineChartViewBase).calculateOffsets() + view.setNeedsDisplay() + } +} \ No newline at end of file diff --git a/Charts/Classes/Utils/ChartViewPortHandler.swift b/Charts/Classes/Utils/ChartViewPortHandler.swift index a6e664d6b6..06b4504f51 100755 --- a/Charts/Classes/Utils/ChartViewPortHandler.swift +++ b/Charts/Classes/Utils/ChartViewPortHandler.swift @@ -173,6 +173,12 @@ public class ChartViewPortHandler: NSObject // MARK: - Scaling/Panning etc. + /// Zooms by the specified zoom factors. + public func zoom(scaleX scaleX: CGFloat, scaleY: CGFloat) -> CGAffineTransform + { + return CGAffineTransformScale(_touchMatrix, scaleX, scaleY) + } + /// Zooms around the specified center public func zoom(scaleX scaleX: CGFloat, scaleY: CGFloat, x: CGFloat, y: CGFloat) -> CGAffineTransform { @@ -194,6 +200,27 @@ public class ChartViewPortHandler: NSObject return zoom(scaleX: 0.7, scaleY: 0.7, x: x, y: y) } + /// Sets the scale factor to the specified values. + public func setZoom(scaleX scaleX: CGFloat, scaleY: CGFloat) -> CGAffineTransform + { + var matrix = _touchMatrix + matrix.a = scaleX + matrix.d = scaleY + return matrix + } + + /// Sets the scale factor to the specified values. x and y is pivot. + public func setZoom(scaleX scaleX: CGFloat, scaleY: CGFloat, x: CGFloat, y: CGFloat) -> CGAffineTransform + { + var matrix = _touchMatrix + matrix.a = 1.0 + matrix.d = 1.0 + matrix = CGAffineTransformTranslate(matrix, x, y) + matrix = CGAffineTransformScale(matrix, scaleX, scaleY) + matrix = CGAffineTransformTranslate(matrix, -x, -y) + return matrix + } + /// Resets all zooming and dragging and makes the chart fit exactly it's bounds. public func fitScreen() -> CGAffineTransform { @@ -203,7 +230,20 @@ public class ChartViewPortHandler: NSObject return CGAffineTransformIdentity } + /// Translates to the specified point. + public func translate(pt pt: CGPoint) -> CGAffineTransform + { + let translateX = pt.x - offsetLeft + let translateY = pt.y - offsetTop + + let matrix = CGAffineTransformConcat(_touchMatrix, CGAffineTransformMakeTranslation(-translateX, -translateY)) + + return matrix + } + /// Centers the viewport around the specified position (x-index and y-value) in the chart. + /// Centering the viewport outside the bounds of the chart is not possible. + /// Makes most sense in combination with the setScaleMinima(...) method. public func centerViewPort(pt pt: CGPoint, chart: ChartViewBase) { let translateX = pt.x - offsetLeft diff --git a/ChartsDemo/Classes/Demos/LineChart2ViewController.m b/ChartsDemo/Classes/Demos/LineChart2ViewController.m index 8cf162319d..da8a3abb64 100644 --- a/ChartsDemo/Classes/Demos/LineChart2ViewController.m +++ b/ChartsDemo/Classes/Demos/LineChart2ViewController.m @@ -253,6 +253,11 @@ - (IBAction)slidersValueChanged:(id)sender - (void)chartValueSelected:(ChartViewBase * __nonnull)chartView entry:(ChartDataEntry * __nonnull)entry dataSetIndex:(NSInteger)dataSetIndex highlight:(ChartHighlight * __nonnull)highlight { NSLog(@"chartValueSelected"); + + [_chartView centerViewToAnimatedWithXIndex:entry.xIndex yValue:entry.value axis:[_chartView.data getDataSetByIndex:dataSetIndex].axisDependency duration:1.0]; + //[_chartView moveViewToAnimatedWithXIndex:entry.xIndex yValue:entry.value axis:[_chartView.data getDataSetByIndex:dataSetIndex].axisDependency duration:1.0]; + //[_chartView zoomAndCenterViewAnimatedWithScaleX:1.8 scaleY:1.8 xIndex:entry.xIndex yValue:entry.value axis:[_chartView.data getDataSetByIndex:dataSetIndex].axisDependency duration:1.0]; + } - (void)chartValueNothingSelected:(ChartViewBase * __nonnull)chartView