Обрезка градиента CG для CGPath - PullRequest
1 голос
/ 26 января 2011

Я долго бился головой о стену, пытаясь понять, почему это не работает.

По сути, я пытаюсь построить график (диаграмму) с помощью CGPath, а затем использовать его для обрезки градиента. Конечный эффект должен быть похож на приложение Stocks, которое поставляется с iPhone.

Градиент и траектория хорошо рисуются по отдельности в виде двух слоистых (незакрепленных) элементов. Но если я закомментирую CGContextDrawPath, ни линия, ни градиент не появятся на экране.

Вот мой код drawRect:

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 Ответ

1 голос
/ 26 января 2011
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

Это просто график серии отрезков.Для определенных значений точек это может выглядеть примерно так:

| |||| |

Он не отображает одну непрерывную фигуру.Таким образом, этот путь бесполезен для отсечения, поскольку он фактически пуст;как вы уже видели, отсечение к нему не приведет к тому, что в пути отсечения больше не будет рисунка.

Каждый lineto, curveto, arc и т. д. работает с текущей точки.Вы устанавливаете, что изначально с moveto, но каждый lineto, curveto, arc и т. Д. Не очищает текущую точку, он обновляет это.Таким образом, чтобы создать одну фигуру, вы делаете одну moveto, за которой следует последовательность lineto (или curveto или arc), за которой в конечном итоге следует closepath.

Говоря о closepath

CGPathCloseSubpath(thePath);

Это создает единственную замкнутую фигуру на пути, но так как эта фигура имеет нулевую область (будучи только отрезком линии, который удваивается), она все еще остаетсябесполезно для отсечения.

Я подозреваю, что все, что вам нужно сделать, это вырезать хотя бы один из сегментов moveto (тот, что в цикле, если не тот, что после него).Затем вы можете упростить цикл - используйте быстрое перечисление в массиве вместо индексов и отключите отслеживание «последней нарисованной точки».

Кроме того, правильный тип для индексов в NSArray:NSUInteger, а не int.Следите за тем, чтобы ваши типы соответствовали друг другу - это поможет избежать боли в будущем.

...