From e056480b819028b301f882335c419f80ea25f315 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 7 May 2015 16:12:07 +0300 Subject: [PATCH] A two finger rotation recogniser for Pie/Radar (Good for scrollviews) (issue #65) This will play nice with other gesture recognizers, especially good for when inside a scrollview. It is disabled by default. --- Charts/Classes/Charts/ChartViewBase.swift | 7 - .../Charts/PieRadarChartViewBase.swift | 144 ++++++++++++++---- 2 files changed, 118 insertions(+), 33 deletions(-) diff --git a/Charts/Classes/Charts/ChartViewBase.swift b/Charts/Classes/Charts/ChartViewBase.swift index acfc8d8c89..f04a4a5ca1 100644 --- a/Charts/Classes/Charts/ChartViewBase.swift +++ b/Charts/Classes/Charts/ChartViewBase.swift @@ -317,13 +317,6 @@ public class ChartViewBase: UIView, ChartAnimatorDelegate ChartUtils.drawText(context: context, text: descriptionText, point: CGPoint(x: frame.width - _viewPortHandler.offsetRight - 10.0, y: frame.height - _viewPortHandler.offsetBottom - 10.0 - font!.lineHeight), align: .Right, attributes: attrs); } - /// enables/disables defauilt touch events to be handled. When disable, touches are not passed to parent views so scrolling inside a UIScrollView won't work. - public var defaultTouchEventsEnabled: Bool - { - get { return _interceptTouchEvents; } - set { _interceptTouchEvents = true; } - } - // MARK: - Highlighting /// Returns the array of currently highlighted values. This might be null or empty if nothing is highlighted. diff --git a/Charts/Classes/Charts/PieRadarChartViewBase.swift b/Charts/Classes/Charts/PieRadarChartViewBase.swift index cd6e4e7375..ad015b53d6 100644 --- a/Charts/Classes/Charts/PieRadarChartViewBase.swift +++ b/Charts/Classes/Charts/PieRadarChartViewBase.swift @@ -19,15 +19,18 @@ import UIKit.UIGestureRecognizer public class PieRadarChartViewBase: ChartViewBase { /// holds the normalized version of the current rotation angle of the chart - private var _rotationAngle = CGFloat(270.0); + private var _rotationAngle = CGFloat(270.0) /// holds the raw version of the current rotation angle of the chart - private var _rawRotationAngle = CGFloat(270.0); + private var _rawRotationAngle = CGFloat(270.0) /// flag that indicates if rotation is enabled or not public var rotationEnabled = true + private var _rotationWithTwoFingers = false + private var _tapGestureRecognizer: UITapGestureRecognizer! + private var _rotationGestureRecognizer: UIRotationGestureRecognizer! public override init(frame: CGRect) { @@ -49,7 +52,14 @@ public class PieRadarChartViewBase: ChartViewBase super.initialize(); _tapGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("tapGestureRecognized:")); + _rotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: Selector("rotationGestureRecognized:")); + self.addGestureRecognizer(_tapGestureRecognizer); + + if (rotationWithTwoFingers) + { + self.addGestureRecognizer(_rotationGestureRecognizer); + } } internal override func calcMinMax() @@ -348,6 +358,41 @@ public class PieRadarChartViewBase: ChartViewBase public var isRotationEnabled: Bool { return rotationEnabled; } + /// flag that indicates if rotation is done with two fingers or one. + /// when the chart is inside a scrollview, you need a two-finger rotation because a one-finger rotation eats up all touch events. + /// :default: false + public var rotationWithTwoFingers: Bool + { + get + { + return _rotationWithTwoFingers; + } + set + { + if (newValue != _rotationWithTwoFingers) + { + _rotationWithTwoFingers = newValue; + + if (_rotationWithTwoFingers) + { + self.addGestureRecognizer(_rotationGestureRecognizer); + } + else + { + self.removeGestureRecognizer(_rotationGestureRecognizer); + } + } + } + } + + /// flag that indicates if rotation is done with two fingers or one. + /// when the chart is inside a scrollview, you need a two-finger rotation because a one-finger rotation eats up all touch events. + /// :default: false + public var isRotationWithTwoFingers: Bool + { + return _rotationWithTwoFingers; + } + // MARK: - Animation private var _spinAnimator: ChartAnimator!; @@ -408,35 +453,39 @@ public class PieRadarChartViewBase: ChartViewBase public override func touchesBegan(touches: Set, withEvent event: UIEvent) { - super.touchesBegan(touches, withEvent: event); - // if rotation by touch is enabled if (rotationEnabled) { - var touch = touches.first as! UITouch!; - stopDeceleration(); - var touchLocation = touch.locationInView(self); - - self.resetVelocity(); - - if (rotationEnabled) + if (!rotationWithTwoFingers) { - self.sampleVelocity(touchLocation: touchLocation); + var touch = touches.first as! UITouch!; + + var touchLocation = touch.locationInView(self); + + self.resetVelocity(); + + if (rotationEnabled) + { + self.sampleVelocity(touchLocation: touchLocation); + } + + self.setGestureStartAngle(x: touchLocation.x, y: touchLocation.y); + + _touchStartPoint = touchLocation; } - - self.setGestureStartAngle(x: touchLocation.x, y: touchLocation.y); - - _touchStartPoint = touchLocation; + } + + if (!_isRotating) + { + super.touchesBegan(touches, withEvent: event); } } public override func touchesMoved(touches: Set, withEvent event: UIEvent) { - super.touchesMoved(touches, withEvent: event); - - if (rotationEnabled) + if (rotationEnabled && !rotationWithTwoFingers) { var touch = touches.first as! UITouch!; @@ -450,9 +499,6 @@ public class PieRadarChartViewBase: ChartViewBase if (!_isRotating && distance(eventX: touchLocation.x, startX: _touchStartPoint.x, eventY: touchLocation.y, startY: _touchStartPoint.y) > CGFloat(8.0)) { _isRotating = true; - - _defaultTouchEventsWereEnabled = self.defaultTouchEventsEnabled; - self.defaultTouchEventsEnabled = false; } else { @@ -460,13 +506,21 @@ public class PieRadarChartViewBase: ChartViewBase setNeedsDisplay(); } } + + if (!_isRotating) + { + super.touchesMoved(touches, withEvent: event); + } } public override func touchesEnded(touches: Set, withEvent event: UIEvent) { - super.touchesEnded(touches, withEvent: event); + if (!_isRotating) + { + super.touchesEnded(touches, withEvent: event); + } - if (rotationEnabled) + if (rotationEnabled && !rotationWithTwoFingers) { var touch = touches.first as! UITouch!; @@ -488,9 +542,9 @@ public class PieRadarChartViewBase: ChartViewBase } } } + if (_isRotating) { - self.defaultTouchEventsEnabled = _defaultTouchEventsWereEnabled; _isRotating = false; } } @@ -501,7 +555,6 @@ public class PieRadarChartViewBase: ChartViewBase if (_isRotating) { - self.defaultTouchEventsEnabled = _defaultTouchEventsWereEnabled; _isRotating = false; } } @@ -712,4 +765,43 @@ public class PieRadarChartViewBase: ChartViewBase } } } + + @objc private func rotationGestureRecognized(recognizer: UIRotationGestureRecognizer) + { + if (recognizer.state == UIGestureRecognizerState.Began) + { + stopDeceleration(); + + _startAngle = self.rawRotationAngle; + } + + if (recognizer.state == UIGestureRecognizerState.Began || recognizer.state == UIGestureRecognizerState.Changed) + { + var angle = ChartUtils.Math.FRAD2DEG * recognizer.rotation; + + self.rotationAngle = _startAngle + angle; + setNeedsDisplay(); + } + else if (recognizer.state == UIGestureRecognizerState.Ended) + { + var angle = ChartUtils.Math.FRAD2DEG * recognizer.rotation; + + self.rotationAngle = _startAngle + angle; + setNeedsDisplay(); + + if (isDragDecelerationEnabled) + { + stopDeceleration(); + + _decelerationAngularVelocity = ChartUtils.Math.FRAD2DEG * recognizer.velocity; + + if (_decelerationAngularVelocity != 0.0) + { + _decelerationLastTime = CACurrentMediaTime(); + _decelerationDisplayLink = CADisplayLink(target: self, selector: Selector("decelerationLoop")); + _decelerationDisplayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes); + } + } + } + } } \ No newline at end of file