UIView坐标系对UIProgressView循环版的影响

时间:2014-06-04 20:42:55

标签: ios uiview uikit cgcontext uiprogressview

我需要一个派生自UIView的类,它通过填充一个越来越大的圆圈来表示进度(当进度从0变为1时,它的角度应增加2 PI)。它就像是UIProgressView的循环版本。

以下代码还没有完成这项工作:它产生了越来越多的小扇区,这些扇区以闪烁的斑马图案堆叠。据推测,代码正在弄乱其坐标与UIView flipping的坐标系,CGContextAddArc(显然)反直觉定义为“顺时针”等等。

错误行为的原因是什么?如何解决?

#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;
}

1 个答案:

答案 0 :(得分:1)

您需要知道的最重要的事情是正Y向下,这使得角度和顺时针方向不直观。你有两个选择,要么只是随身携带,要么在绘制任何东西之前翻转Y轴。在下面的示例代码中,我采用了后一种方法。

drawRect方法不是累积的。换句话说,您需要在每次调用drawRect时绘制整个扇区。因此没有理由跟踪起始角度,你应该始终从角度PI / 2开始并绘制整个扇区。

无需保存和恢复图形状态。每次调用drawRect时都会创建新的图形上下文。

UIColor类具有setsetFillsetStroke方法,可让您在不使用CGColors的情况下更改填充和描边颜色。

考虑到所有这些,这就是代码的样子,假设self.progress是介于0.0和1.0之间的数字

- (void)drawRect:(CGRect)rect
{
    if ( self.progress <= 0 || self.progress > 1.0 )
        return;

    CGFloat x = self.bounds.size.width  / 2.0;
    CGFloat y = self.bounds.size.height / 2.0;
    CGFloat r = (x <= y) ? x : y;

    CGFloat angle = M_PI_2 - self.progress * 2.0 * M_PI;

    CGContextRef context = UIGraphicsGetCurrentContext();

    // fix the y axis, so that positive y is up
    CGContextScaleCTM( context, 1.0, -1.0 );
    CGContextTranslateCTM( context, 0, -self.bounds.size.height );

    // draw the pie shape
    [self.sectorTintColor setFill];
    CGContextMoveToPoint( context, x, y );
    CGContextAddArc( context, x, y, r, M_PI_2, angle, YES );
    CGContextClosePath( context );

    CGContextFillPath( context );
}