I need a class derived from UIView
that indicates progress by filling an increasingly large sector of a circle (as progress goes from 0 to 1 its angle should grow by 2 PI). It's like a circular version of UIProgressView
.
The following code does not yet quite do the job: it produces increasingly many small sectors stacked in a flickering zebra pattern. Presumably the code is messing up its coordinates with UIView flipping its coordinate system, with CGContextAddArc
´s (apparently) counter-intuitive definition of "clockwise", etc.
What's the reason for the misbehavior and how can it be fixed?
#define PERCENTAGE_TO_RADIANS(PERCENTAGE) ((PERCENTAGE) * 2 * M_PI - M_PI_2)
- (void)drawRect:(CGRect)rect
{
NSAssert(!self.opaque, nil);
NSAssert(!self.clearsContextBeforeDrawing, nil);
CGSize size = self.bounds.size;
CGPoint center = [self.superview convertPoint:self.center toView:self];
NSAssert(size.width == size.height, nil);
NSAssert((self.progress >= 0) && (self.progress <= 1), nil);
NSAssert(PERCENTAGE_TO_RADIANS(0 ) == -M_PI_2 , nil); // 0% progress corresponds to north
NSAssert(PERCENTAGE_TO_RADIANS(0.25) == 0 , nil); // 25% progress corresponds to east
NSAssert(PERCENTAGE_TO_RADIANS(0.5 ) == M_PI_2 , nil); // 50% progress corresponds to south
NSAssert(PERCENTAGE_TO_RADIANS(0.75) == M_PI , nil); // 75% progress corresponds to west
NSAssert(PERCENTAGE_TO_RADIANS(1 ) == 3 * M_PI_2, nil); // 100% progress corresponds to north
CGFloat x = center.x;
CGFloat y = center.y;
CGFloat radius = size.width / 2.0;
CGFloat startAngle = self.lastAngle;
CGFloat endAngle = PERCENTAGE_TO_RADIANS(self.progress);
int clockwise = 0;
if (self.progress > 0) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetFillColorWithColor(context, self.sectorTintColor.CGColor);
CGContextSetLineWidth(context, 1);
NSAssert(startAngle <= endAngle, nil);
CGContextMoveToPoint(context, x, y);
CGContextAddArc(context, x, y, radius, startAngle, endAngle, clockwise);
CGContextClosePath(context);
CGContextFillPath(context);
CGContextRestoreGState(context);
}
self.lastAngle = endAngle;
}