From 0467bde5549f3bdf915475887d433159f1f3da94 Mon Sep 17 00:00:00 2001 From: Tres Wong-Godfrey Date: Mon, 1 Jun 2015 12:34:58 -0700 Subject: [PATCH 1/3] Mod: Allow Gesture Recognizer To Function Simultaneously With Other GRs This modification ensures that apps depending upon this library which have other gesture recognizers in use during the display of BEMSimpleLineGraphView will be able to utilize delegate methods -lineGraph:didTouchGraphWithClosestIndex: and -lineGraph:didReleaseTouchFromGraphWithClosestIndex: Without responding to the UIGestureRecognizer delegate method changes contained in this commit, the BEMSimpleLineGraphView delegate methods are never called. --- Classes/BEMSimpleLineGraphView.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Classes/BEMSimpleLineGraphView.m b/Classes/BEMSimpleLineGraphView.m index cefe8c8..981541e 100644 --- a/Classes/BEMSimpleLineGraphView.m +++ b/Classes/BEMSimpleLineGraphView.m @@ -1298,6 +1298,14 @@ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { } else return NO; } +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ + return YES; +} +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{ + return YES; +} + + - (void)handleGestureAction:(UIGestureRecognizer *)recognizer { CGPoint translation = [recognizer locationInView:self.viewForBaselineLayout]; From 7bfaa2e71dc8bf96c71bdced6ddce76853d160a9 Mon Sep 17 00:00:00 2001 From: Tres Wong-Godfrey Date: Mon, 1 Jun 2015 16:43:04 -0700 Subject: [PATCH 2/3] Mod: Make Displayed Graph Adhere To Underlying Dataset This modifies the algorithm used to determine the graph's line. The old algorithm, although generating prettier graphs, displayed inaccurate information; line arcs would go above and below min/max values & a graph that had two data points of the same value in a row would show invalid arcs between the two data points (always upward) giving the user an inaccurate representation of the data. --- Classes/BEMLine.m | 280 +++++++++++++++++++++++----------------------- 1 file changed, 143 insertions(+), 137 deletions(-) diff --git a/Classes/BEMLine.m b/Classes/BEMLine.m index 021baf2..20903d7 100644 --- a/Classes/BEMLine.m +++ b/Classes/BEMLine.m @@ -16,6 +16,11 @@ #define CGFloatValue floatValue #endif +@interface BEMLine() +@property(nonatomic, retain) NSMutableArray *points; + +@end + @implementation BEMLine - (instancetype)initWithFrame:(CGRect)frame { @@ -37,44 +42,44 @@ - (void)drawRect:(CGRect)rect { UIBezierPath *verticalReferenceLinesPath = [UIBezierPath bezierPath]; UIBezierPath *horizontalReferenceLinesPath = [UIBezierPath bezierPath]; UIBezierPath *referenceFramePath = [UIBezierPath bezierPath]; - + verticalReferenceLinesPath.lineCapStyle = kCGLineCapButt; verticalReferenceLinesPath.lineWidth = 0.7; - + horizontalReferenceLinesPath.lineCapStyle = kCGLineCapButt; horizontalReferenceLinesPath.lineWidth = 0.7; - + referenceFramePath.lineCapStyle = kCGLineCapButt; referenceFramePath.lineWidth = 0.7; - + if (self.enableRefrenceFrame == YES) { if (self.enableBottomReferenceFrameLine) { // Bottom Line [referenceFramePath moveToPoint:CGPointMake(0, self.frame.size.height)]; [referenceFramePath addLineToPoint:CGPointMake(self.frame.size.width, self.frame.size.height)]; } - + if (self.enableLeftReferenceFrameLine) { // Left Line [referenceFramePath moveToPoint:CGPointMake(0+self.lineWidth/4, self.frame.size.height)]; [referenceFramePath addLineToPoint:CGPointMake(0+self.lineWidth/4, 0)]; } - + if (self.enableTopReferenceFrameLine) { // Top Line [referenceFramePath moveToPoint:CGPointMake(0+self.lineWidth/4, 0)]; [referenceFramePath addLineToPoint:CGPointMake(self.frame.size.width, 0)]; } - + if (self.enableRightReferenceFrameLine) { // Right Line [referenceFramePath moveToPoint:CGPointMake(self.frame.size.width - self.lineWidth/4, self.frame.size.height)]; [referenceFramePath addLineToPoint:CGPointMake(self.frame.size.width - self.lineWidth/4, 0)]; } - + [referenceFramePath closePath]; } - + if (self.enableRefrenceLines == YES) { if (self.arrayOfVerticalRefrenceLinePoints.count > 0) { for (NSNumber *xNumber in self.arrayOfVerticalRefrenceLinePoints) { @@ -86,31 +91,31 @@ - (void)drawRect:(CGRect)rect { xValue = [xNumber floatValue] - self.verticalReferenceHorizontalFringeNegation; } else xValue = [xNumber floatValue]; } else xValue = [xNumber floatValue]; - + CGPoint initialPoint = CGPointMake(xValue, self.frame.size.height); CGPoint finalPoint = CGPointMake(xValue, 0); - + [verticalReferenceLinesPath moveToPoint:initialPoint]; [verticalReferenceLinesPath addLineToPoint:finalPoint]; } - + [verticalReferenceLinesPath closePath]; } - + if (self.arrayOfHorizontalRefrenceLinePoints.count > 0) { for (NSNumber *yNumber in self.arrayOfHorizontalRefrenceLinePoints) { CGPoint initialPoint = CGPointMake(0, [yNumber floatValue]); CGPoint finalPoint = CGPointMake(self.frame.size.width, [yNumber floatValue]); - + [horizontalReferenceLinesPath moveToPoint:initialPoint]; [horizontalReferenceLinesPath addLineToPoint:finalPoint]; } - + [horizontalReferenceLinesPath closePath]; } } - - + + //----------------------------// //----- Draw Average Line ----// //----------------------------// @@ -118,129 +123,55 @@ - (void)drawRect:(CGRect)rect { if (self.averageLine.enableAverageLine == YES) { averageLinePath.lineCapStyle = kCGLineCapButt; averageLinePath.lineWidth = self.averageLine.width; - + CGPoint initialPoint = CGPointMake(0, self.averageLineYCoordinate); CGPoint finalPoint = CGPointMake(self.frame.size.width, self.averageLineYCoordinate); - + [averageLinePath moveToPoint:initialPoint]; [averageLinePath addLineToPoint:finalPoint]; - + [averageLinePath closePath]; } - - + + //----------------------------// //------ Draw Graph Line -----// //----------------------------// - CGPoint CP1; - CGPoint CP2; - - // BEZIER CURVE - if (self.bezierCurveIsEnabled == YES) { - // First control point - CP1 = CGPointMake(self.P1.x + (self.P2.x - self.P1.x)/3, - self.P1.y - (self.P1.y - self.P2.y)/3 - (self.P0.y - self.P1.y)*0.3); - - // Second control point - CP2 = CGPointMake(self.P1.x + 2*(self.P2.x - self.P1.x)/3, - (self.P1.y - 2*(self.P1.y - self.P2.y)/3) + (self.P2.y - self.P3.y)*0.3); - } - // LINE UIBezierPath *line = [UIBezierPath bezierPath]; - UIBezierPath *fillTop = [UIBezierPath bezierPath]; - UIBezierPath *fillBottom = [UIBezierPath bezierPath]; - - CGPoint p0; - CGPoint p1; - CGPoint p2; - CGPoint p3; - CGFloat tensionBezier1 = 0.3; - CGFloat tensionBezier2 = 0.3; + UIBezierPath *fillTop; + UIBezierPath *fillBottom; + + CGFloat xIndexScale = self.frame.size.width/([self.arrayOfPoints count] - 1); - - [fillBottom moveToPoint:CGPointMake(self.frame.size.width, self.frame.size.height)]; - [fillBottom addLineToPoint:CGPointMake(0, self.frame.size.height)]; - [fillTop moveToPoint:CGPointMake(self.frame.size.width, 0)]; - [fillTop addLineToPoint:CGPointMake(0, 0)]; - - NSMutableArray *points = [NSMutableArray arrayWithCapacity:self.arrayOfPoints.count]; + + + self.points = [NSMutableArray arrayWithCapacity:self.arrayOfPoints.count]; for (int i = 0; i < self.arrayOfPoints.count; i++) { CGPoint value = CGPointMake(xIndexScale * i, [self.arrayOfPoints[i] CGFloatValue]); if (value.y != BEMNullGraphValue || !self.interpolateNullValues) { - [points addObject:[NSValue valueWithCGPoint:value]]; + [self.points addObject:[NSValue valueWithCGPoint:value]]; } } - - CGPoint previousPoint1; - CGPoint previousPoint2; - - for (int i = 0; i < points.count - 1; i++) { - p1 = [[points objectAtIndex:i] CGPointValue]; - p2 = [[points objectAtIndex:i + 1] CGPointValue]; - - if (!self.interpolateNullValues && (p1.y == BEMNullGraphValue || p2.y == BEMNullGraphValue)) continue; - - if (self.disableMainLine == NO) [line moveToPoint:p1]; - [fillBottom addLineToPoint:p1]; - [fillTop addLineToPoint:p1]; - - if (self.bezierCurveIsEnabled == YES) { - const CGFloat maxTension = 1.0f / 3.0f; - tensionBezier1 = maxTension; - tensionBezier2 = maxTension; - - if (i > 0) { // Exception for first line because there is no previous point - p0 = previousPoint1; - if (p2.y - p1.y == p1.y - p0.y) tensionBezier1 = 0; - } else { - tensionBezier1 = 0; - p0 = p1; - } - - if (i < points.count - 2) { // Exception for last line because there is no next point - p3 = [[points objectAtIndex:i + 2] CGPointValue]; - if (p3.y - p2.y == p2.y - p1.y) tensionBezier2 = 0; - } else { - p3 = p2; - tensionBezier2 = 0; - } - - // The tension should never exceed 0.3 - if (tensionBezier1 > maxTension) tensionBezier1 = maxTension; - if (tensionBezier2 > maxTension) tensionBezier2 = maxTension; - - // First control point - CP1 = CGPointMake(p1.x + (p2.x - p1.x)/3, - p1.y - (p1.y - p2.y)/3 - (p0.y - p1.y)*tensionBezier1); - - // Second control point - CP2 = CGPointMake(p1.x + 2*(p2.x - p1.x)/3, - (p1.y - 2*(p1.y - p2.y)/3) + (p2.y - p3.y)*tensionBezier2); - - if (self.disableMainLine == NO) [line addCurveToPoint:p2 controlPoint1:CP1 controlPoint2:CP2]; - [fillBottom addCurveToPoint:p2 controlPoint1:CP1 controlPoint2:CP2]; - [fillTop addCurveToPoint:p2 controlPoint1:CP1 controlPoint2:CP2]; - } else { - if (self.disableMainLine == NO) [line addLineToPoint:p2]; - [fillBottom addLineToPoint:p2]; - [fillTop addLineToPoint:p2]; - } - - previousPoint1 = p1; - previousPoint2 = p2; + + if (!self.disableMainLine && self.bezierCurveIsEnabled) { + line = [BEMLine quadCurvedPathWithPoints:self.points]; + fillBottom = [BEMLine quadCurvedPathWithPoints:self.bottomPointsArray]; + fillTop = [BEMLine quadCurvedPathWithPoints:self.topPointsArray]; + } else { + fillBottom = [BEMLine linesToPoints:self.bottomPointsArray]; + fillTop = [BEMLine linesToPoints:self.topPointsArray]; } - - + //----------------------------// //----- Draw Fill Colors -----// //----------------------------// [self.topColor set]; [fillTop fillWithBlendMode:kCGBlendModeNormal alpha:self.topAlpha]; - + [self.bottomColor set]; [fillBottom fillWithBlendMode:kCGBlendModeNormal alpha:self.bottomAlpha]; - + CGContextRef ctx = UIGraphicsGetCurrentContext(); if (self.topGradient != nil) { CGContextSaveGState(ctx); @@ -249,7 +180,7 @@ - (void)drawRect:(CGRect)rect { CGContextDrawLinearGradient(ctx, self.topGradient, CGPointZero, CGPointMake(0, CGRectGetMaxY(fillTop.bounds)), 0); CGContextRestoreGState(ctx); } - + if (self.bottomGradient != nil) { CGContextSaveGState(ctx); CGContextAddPath(ctx, [fillBottom CGPath]); @@ -257,8 +188,8 @@ - (void)drawRect:(CGRect)rect { CGContextDrawLinearGradient(ctx, self.bottomGradient, CGPointZero, CGPointMake(0, CGRectGetMaxY(fillBottom.bounds)), 0); CGContextRestoreGState(ctx); } - - + + //----------------------------// //------ Animate Drawing -----// //----------------------------// @@ -269,22 +200,22 @@ - (void)drawRect:(CGRect)rect { verticalReferenceLinesPathLayer.opacity = self.lineAlpha == 0 ? 0.1 : self.lineAlpha/2; verticalReferenceLinesPathLayer.fillColor = nil; verticalReferenceLinesPathLayer.lineWidth = self.lineWidth/2; - + if (self.lineDashPatternForReferenceYAxisLines) { verticalReferenceLinesPathLayer.lineDashPattern = self.lineDashPatternForReferenceYAxisLines; } - + if (self.refrenceLineColor) { verticalReferenceLinesPathLayer.strokeColor = self.refrenceLineColor.CGColor; } else { verticalReferenceLinesPathLayer.strokeColor = self.color.CGColor; } - + if (self.animationTime > 0) [self animateForLayer:verticalReferenceLinesPathLayer withAnimationType:self.animationType isAnimatingReferenceLine:YES]; [self.layer addSublayer:verticalReferenceLinesPathLayer]; - - + + CAShapeLayer *horizontalReferenceLinesPathLayer = [CAShapeLayer layer]; horizontalReferenceLinesPathLayer.frame = self.bounds; horizontalReferenceLinesPathLayer.path = horizontalReferenceLinesPath.CGPath; @@ -294,32 +225,32 @@ - (void)drawRect:(CGRect)rect { if(self.lineDashPatternForReferenceXAxisLines) { horizontalReferenceLinesPathLayer.lineDashPattern = self.lineDashPatternForReferenceXAxisLines; } - + if (self.refrenceLineColor) { horizontalReferenceLinesPathLayer.strokeColor = self.refrenceLineColor.CGColor; } else { horizontalReferenceLinesPathLayer.strokeColor = self.color.CGColor; } - + if (self.animationTime > 0) [self animateForLayer:horizontalReferenceLinesPathLayer withAnimationType:self.animationType isAnimatingReferenceLine:YES]; [self.layer addSublayer:horizontalReferenceLinesPathLayer]; } - + CAShapeLayer *referenceLinesPathLayer = [CAShapeLayer layer]; referenceLinesPathLayer.frame = self.bounds; referenceLinesPathLayer.path = referenceFramePath.CGPath; referenceLinesPathLayer.opacity = self.lineAlpha == 0 ? 0.1 : self.lineAlpha/2; referenceLinesPathLayer.fillColor = nil; referenceLinesPathLayer.lineWidth = self.lineWidth/2; - + if (self.refrenceLineColor) referenceLinesPathLayer.strokeColor = self.refrenceLineColor.CGColor; else referenceLinesPathLayer.strokeColor = self.color.CGColor; - + if (self.animationTime > 0) [self animateForLayer:referenceLinesPathLayer withAnimationType:self.animationType isAnimatingReferenceLine:YES]; [self.layer addSublayer:referenceLinesPathLayer]; - + if (self.disableMainLine == NO) { CAShapeLayer *pathLayer = [CAShapeLayer layer]; pathLayer.frame = self.bounds; @@ -334,7 +265,7 @@ - (void)drawRect:(CGRect)rect { if (self.lineGradient) [self.layer addSublayer:[self backgroundGradientLayerForLayer:pathLayer]]; else [self.layer addSublayer:pathLayer]; } - + if (self.averageLine.enableAverageLine == YES) { CAShapeLayer *averageLinePathLayer = [CAShapeLayer layer]; averageLinePathLayer.frame = self.bounds; @@ -342,18 +273,93 @@ - (void)drawRect:(CGRect)rect { averageLinePathLayer.opacity = self.averageLine.alpha; averageLinePathLayer.fillColor = nil; averageLinePathLayer.lineWidth = self.averageLine.width; - + if (self.averageLine.dashPattern) averageLinePathLayer.lineDashPattern = self.averageLine.dashPattern; - + if (self.averageLine.color) averageLinePathLayer.strokeColor = self.averageLine.color.CGColor; else averageLinePathLayer.strokeColor = self.color.CGColor; - + if (self.animationTime > 0) [self animateForLayer:averageLinePathLayer withAnimationType:self.animationType isAnimatingReferenceLine:NO]; [self.layer addSublayer:averageLinePathLayer]; } } +-(NSArray *) topPointsArray { + CGPoint topPointZero = CGPointMake(0,0); + CGPoint topPointFull = CGPointMake(self.frame.size.width, 0); + NSMutableArray *topPoints = [NSMutableArray arrayWithArray:self.points]; + [topPoints insertObject:[NSValue valueWithCGPoint:topPointZero] atIndex:0]; + [topPoints addObject:[NSValue valueWithCGPoint:topPointFull]]; + return topPoints; +} + +-(NSArray *) bottomPointsArray { + CGPoint bottomPointZero = CGPointMake(0, self.frame.size.height); + CGPoint bottomPointFull = CGPointMake(self.frame.size.width, self.frame.size.height); + NSMutableArray *bottomPoints = [NSMutableArray arrayWithArray:self.points]; + [bottomPoints insertObject:[NSValue valueWithCGPoint:bottomPointZero] atIndex:0]; + [bottomPoints addObject:[NSValue valueWithCGPoint:bottomPointFull]]; + return bottomPoints; +} + ++ (UIBezierPath *) linesToPoints:(NSArray *) points { + UIBezierPath *path = [UIBezierPath bezierPath]; + NSValue *value = points[0]; + CGPoint p1 = [value CGPointValue]; + [path moveToPoint:p1]; + + for (NSUInteger i = 1; i < points.count; i++) { + value = points[i]; + CGPoint p2 = [value CGPointValue]; + [path addLineToPoint:p2]; + } + return path; +} + ++ (UIBezierPath *)quadCurvedPathWithPoints:(NSArray *)points { + UIBezierPath *path = [UIBezierPath bezierPath]; + + NSValue *value = points[0]; + CGPoint p1 = [value CGPointValue]; + [path moveToPoint:p1]; + + if (points.count == 2) { + value = points[1]; + CGPoint p2 = [value CGPointValue]; + [path addLineToPoint:p2]; + return path; + } + + for (NSUInteger i = 1; i < points.count; i++) { + value = points[i]; + CGPoint p2 = [value CGPointValue]; + + CGPoint midPoint = midPointForPoints(p1, p2); + [path addQuadCurveToPoint:midPoint controlPoint:controlPointForPoints(midPoint, p1)]; + [path addQuadCurveToPoint:p2 controlPoint:controlPointForPoints(midPoint, p2)]; + + p1 = p2; + } + return path; +} + +static CGPoint midPointForPoints(CGPoint p1, CGPoint p2) { + return CGPointMake((p1.x + p2.x) / 2, (p1.y + p2.y) / 2); +} + +static CGPoint controlPointForPoints(CGPoint p1, CGPoint p2) { + CGPoint controlPoint = midPointForPoints(p1, p2); + CGFloat diffY = abs(p2.y - controlPoint.y); + + if (p1.y < p2.y) + controlPoint.y += diffY; + else if (p1.y > p2.y) + controlPoint.y -= diffY; + + return controlPoint; +} + - (void)animateForLayer:(CAShapeLayer *)shapeLayer withAnimationType:(BEMLineAnimation)animationType isAnimatingReferenceLine:(BOOL)shouldHalfOpacity { if (animationType == BEMLineAnimationNone) return; else if (animationType == BEMLineAnimationFade) { @@ -363,7 +369,7 @@ - (void)animateForLayer:(CAShapeLayer *)shapeLayer withAnimationType:(BEMLineAni if (shouldHalfOpacity == YES) pathAnimation.toValue = [NSNumber numberWithFloat:self.lineAlpha == 0 ? 0.1 : self.lineAlpha/2]; else pathAnimation.toValue = [NSNumber numberWithFloat:self.lineAlpha]; [shapeLayer addAnimation:pathAnimation forKey:@"opacity"]; - + return; } else if (animationType == BEMLineAnimationExpand) { CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"lineWidth"]; @@ -371,7 +377,7 @@ - (void)animateForLayer:(CAShapeLayer *)shapeLayer withAnimationType:(BEMLineAni pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f]; pathAnimation.toValue = [NSNumber numberWithFloat:shapeLayer.lineWidth]; [shapeLayer addAnimation:pathAnimation forKey:@"lineWidth"]; - + return; } else { CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; @@ -379,7 +385,7 @@ - (void)animateForLayer:(CAShapeLayer *)shapeLayer withAnimationType:(BEMLineAni pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f]; pathAnimation.toValue = [NSNumber numberWithFloat:1.0f]; [shapeLayer addAnimation:pathAnimation forKey:@"strokeEnd"]; - + return; } } @@ -395,7 +401,7 @@ - (CALayer *)backgroundGradientLayerForLayer:(CAShapeLayer *)shapeLayer { start = CGPointMake(CGRectGetMidX(shapeLayer.bounds), 0); end = CGPointMake(CGRectGetMidX(shapeLayer.bounds), CGRectGetMaxY(shapeLayer.bounds)); } - + CGContextDrawLinearGradient(imageCtx, self.lineGradient, start, end, 0); UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); From 2c3a79d023c9000d855a7bb83dd86132f6ef64cb Mon Sep 17 00:00:00 2001 From: Sam Spencer Date: Fri, 31 Jul 2015 14:02:07 -0400 Subject: [PATCH 3/3] Straight Line Fixes; Syntax Cleanup Fixes a bug where straight lines were not drawn. Cleans up syntax and formatting to uphold consistency. --- Classes/BEMLine.m | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Classes/BEMLine.m b/Classes/BEMLine.m index 20903d7..fb1da41 100644 --- a/Classes/BEMLine.m +++ b/Classes/BEMLine.m @@ -142,10 +142,8 @@ - (void)drawRect:(CGRect)rect { UIBezierPath *fillTop; UIBezierPath *fillBottom; - CGFloat xIndexScale = self.frame.size.width/([self.arrayOfPoints count] - 1); - self.points = [NSMutableArray arrayWithCapacity:self.arrayOfPoints.count]; for (int i = 0; i < self.arrayOfPoints.count; i++) { CGPoint value = CGPointMake(xIndexScale * i, [self.arrayOfPoints[i] CGFloatValue]); @@ -158,6 +156,10 @@ - (void)drawRect:(CGRect)rect { line = [BEMLine quadCurvedPathWithPoints:self.points]; fillBottom = [BEMLine quadCurvedPathWithPoints:self.bottomPointsArray]; fillTop = [BEMLine quadCurvedPathWithPoints:self.topPointsArray]; + } else if (!self.disableMainLine && !self.bezierCurveIsEnabled) { + line = [BEMLine linesToPoints:self.points]; + fillBottom = [BEMLine linesToPoints:self.bottomPointsArray]; + fillTop = [BEMLine linesToPoints:self.topPointsArray]; } else { fillBottom = [BEMLine linesToPoints:self.bottomPointsArray]; fillTop = [BEMLine linesToPoints:self.topPointsArray]; @@ -285,7 +287,7 @@ - (void)drawRect:(CGRect)rect { } } --(NSArray *) topPointsArray { +- (NSArray *)topPointsArray { CGPoint topPointZero = CGPointMake(0,0); CGPoint topPointFull = CGPointMake(self.frame.size.width, 0); NSMutableArray *topPoints = [NSMutableArray arrayWithArray:self.points]; @@ -294,7 +296,7 @@ -(NSArray *) topPointsArray { return topPoints; } --(NSArray *) bottomPointsArray { +- (NSArray *)bottomPointsArray { CGPoint bottomPointZero = CGPointMake(0, self.frame.size.height); CGPoint bottomPointFull = CGPointMake(self.frame.size.width, self.frame.size.height); NSMutableArray *bottomPoints = [NSMutableArray arrayWithArray:self.points]; @@ -303,7 +305,7 @@ -(NSArray *) bottomPointsArray { return bottomPoints; } -+ (UIBezierPath *) linesToPoints:(NSArray *) points { ++ (UIBezierPath *)linesToPoints:(NSArray *)points { UIBezierPath *path = [UIBezierPath bezierPath]; NSValue *value = points[0]; CGPoint p1 = [value CGPointValue]; @@ -350,7 +352,7 @@ static CGPoint midPointForPoints(CGPoint p1, CGPoint p2) { static CGPoint controlPointForPoints(CGPoint p1, CGPoint p2) { CGPoint controlPoint = midPointForPoints(p1, p2); - CGFloat diffY = abs(p2.y - controlPoint.y); + CGFloat diffY = fabs(p2.y - controlPoint.y); if (p1.y < p2.y) controlPoint.y += diffY;