Trimming CGGradient to CGPath

I banged my head on the wall for a long time, trying to understand why this did not work.

Basically, I'm trying to build a graph (chart) with CGPath, and then use it for a gradient clip. The end effect should be similar to the Stocks application that comes with the iPhone.

Gradient and trace are highlighted separately in the form of two-layer (loose) elements. But if I comment on CGContextDrawPath, neither the line nor the gradient will access the screen.

Here is my drawRect code:

CGContextRef context = UIGraphicsGetCurrentContext(); [[UIColor whiteColor] set]; CGContextSetLineWidth(context, 2.0f); CGPoint lastDrawnPt = [[points objectAtIndex:0] CGPointValue]; CGPoint firstDrawnPt = lastDrawnPt; //create the path for the gradient CGMutablePathRef thePath = CGPathCreateMutable(); CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom left CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); for (int i=0; i<(points.count-1); i++) { //CGPoint pt1 = [[points objectAtIndex:i] CGPointValue]; CGPoint pt2 = [[points objectAtIndex:i+1] CGPointValue]; if (pt2.x > lastDrawnPt.x+2) { // only draw if we've moved sunstantially to the right //for the gradient CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); CGPathAddLineToPoint(thePath, NULL, pt2.x, pt2.y); lastDrawnPt = pt2; } } //finish the gradient clipping path CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom right CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); CGPathAddLineToPoint(thePath, NULL, firstDrawnPt.x, self.bounds.size.height); // bottom right CGPathCloseSubpath(thePath); //add the gradient clipping path to the context CGContextSaveGState(context); CGContextAddPath(context, thePath); //draw the path float components[4] = {1.0, 1.0, 1.0, 1.0}; CGContextSetStrokeColor(context, components); CGContextDrawPath(context,kCGPathStroke); //clip the path CGContextClip(context); //Draw Gradient UIColor *topColor = [UIColor colorWithRed: 1.0 green:1.0 blue:1.0 alpha:1.0]; UIColor *bottomColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.0]; CGColorRef colorRef[] = { [topColor CGColor], [bottomColor CGColor] }; CFArrayRef colors = CFArrayCreate(NULL, (const void**)colorRef, sizeof(colorRef) / sizeof(CGColorRef), &kCFTypeArrayCallBacks); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, colors, NULL); CFRelease(colorSpace); CFRelease(colors); // Draw a linear gradient from top to bottom CGPoint gradStartPoint = CGPointMake(50.0, self.bounds.size.height); CGPoint gradEndPoint = CGPointMake(50.0, 0.0); CGContextDrawLinearGradient(context, gradient, gradStartPoint, gradEndPoint, 0); CFRelease(gradient); // Cleanup CGColorSpaceRelease(colorSpace); CGContextRestoreGState(context); 
+1
source share
1 answer
 CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom left CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); for (int i=0; i<(points.count-1); i++) { //CGPoint pt1 = [[points objectAtIndex:i] CGPointValue]; CGPoint pt2 = [[points objectAtIndex:i+1] CGPointValue]; if (pt2.x > lastDrawnPt.x+2) { // only draw if we've moved sunstantially to the right //for the gradient CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); CGPathAddLineToPoint(thePath, NULL, pt2.x, pt2.y); lastDrawnPt = pt2; } } //finish the gradient clipping path CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, lastDrawnPt.y); CGPathAddLineToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); // bottom right CGPathMoveToPoint(thePath, NULL, lastDrawnPt.x, self.bounds.size.height); CGPathAddLineToPoint(thePath, NULL, firstDrawnPt.x, self.bounds.size.height); // bottom right 

It simply displays a series of line segments. For certain point values, this might look something like this:

 | |||| | 

It does not display a single continuous form. Thus, this path is useless for clipping because it is virtually empty; as you saw, clipping to it will cause the further drawing not to be inside the clipping path.

Each lineto , curveto , arc , etc. works from the current point. First you set moveto , but each lineto , curveto , arc , etc. It does not clear the current point, but updates it. Thus, to create a single shape, you make one moveto , followed by a sequence of lineto (or curveto or arc ), and then a closepath .

Speaking of closepath ...

 CGPathCloseSubpath(thePath); 

This creates a single closed shape on the way, but since this shape has a zero region (which is only a segment of a line that doubles back onto itself), it still does not help for clipping purposes.

I suspect that all you have to do is cut out at least one of the moveto segments (one in the loop, if not the one after it). You can then simplify the quick enumeration of the loop using an array instead of using indices and cut out the tracking of the "last drawn point".

Also, the correct index type in NSArray is NSUInteger , not int . Keep your types consistent - this helps to avoid pain later on the road.

+1
source

Source: https://habr.com/ru/post/1305796/


All Articles