如何为渐变进度条设置动画

时间:2012-07-23 10:20:12

标签: iphone core-animation

我有一个进度条,我使用重复计时器进行动画制作。它工作得很好,除了动画不像使用核心动画那么顺畅。如果有人有任何建议如何使用核心动画实现它,我将不胜感激!我没有问题使用核心动画来处理更简单的东西,但是动画渐变等让我很难过。

我的代码在这里 -

-(id)initWithWidth:(float)theWidth radius:(float)theRadius progress:(float)theProgress
{
    self = [super initWithFrame:CGRectMake(0, 0, theWidth, theRadius * 2)];
    if (self) {

        _progressBarWidth = theWidth;
        _radius = theRadius;
        _progress = (float)theProgress;

        self.backgroundColor = [UIColor clearColor];

    }
    return self;
}


-(void)setProgress:(float)theProgress
{
    _progress = theProgress;
    [self setNeedsDisplay];
}


-(void)drawRect:(CGRect)rect
{
    // gradient for blue background (pale blue -> dark blue)

    float colours[] = {0.0, 135.0 / 255.0, 237.0 / 255.0, 1.0, 1.0 / 255.0, 24.0 / 255.0, 140.0 / 255.0, 1.0};

    CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
    size_t num_locations = 2;
    CGFloat locations[2] = {0.0, 1.0};
    CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colours, locations, num_locations);

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);

    CGContextMoveToPoint(context, _radius, 0.0);
    CGContextAddArcToPoint(context, _progressBarWidth, 0.0, _progressBarWidth, _radius, _radius);
    CGContextAddArcToPoint(context, _progressBarWidth, 2.0 * _radius, _progressBarWidth - _radius, 2.0 * _radius, _radius);
    CGContextAddArcToPoint(context, 0.0, 2.0 * _radius, 0.0, _radius, _radius);
    CGContextAddArcToPoint(context, 0.0, 0.0, _radius, 0.0, _radius);

    CGContextClosePath(context);
    CGContextClip(context);

    CGPoint startPoint = CGPointMake(_progressBarWidth / 2.0, 0.0);
    CGPoint endPoint = CGPointMake(_progressBarWidth / 2.0, 2.0 * _radius);

    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);

    CGGradientRelease(gradient);
    CGColorSpaceRelease(baseSpace);

    CGContextRestoreGState(context);


    // gradient for progressed element (pale yellow -> dark yellow)

    float colours2[] = {252.0 / 255.0, 224.0 / 255.0, 0.0, 1.0, 246.0 / 255.0, 191.0 / 255.0, 0.0, 1.0};

    baseSpace = CGColorSpaceCreateDeviceRGB();
    gradient = CGGradientCreateWithColorComponents(baseSpace, colours2, locations, num_locations);

    context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);

    // this defines an overall clipping path for the yellow bar (it's got a square end which can protrude when it's near 100%)
    CGContextMoveToPoint(context, _radius, kDefaultLineWidth);
    CGContextAddArcToPoint(context, _progressBarWidth - kDefaultLineWidth, kDefaultLineWidth, _progressBarWidth - kDefaultLineWidth, _radius, _radius - kDefaultLineWidth);
    CGContextAddArcToPoint(context, _progressBarWidth - kDefaultLineWidth, 2.0 * _radius - kDefaultLineWidth, _progressBarWidth - _radius, 2.0 * _radius - kDefaultLineWidth, _radius - kDefaultLineWidth);
    CGContextAddArcToPoint(context, kDefaultLineWidth, 2.0 * _radius - kDefaultLineWidth, kDefaultLineWidth, _radius, _radius - kDefaultLineWidth);
    CGContextAddArcToPoint(context, kDefaultLineWidth, kDefaultLineWidth, _radius, kDefaultLineWidth, _radius - kDefaultLineWidth);

    CGContextClosePath(context);
    CGContextClip(context);

    // now draw the yellow bar
    float progressWidth = _progress * _progressBarWidth;

    CGContextMoveToPoint(context, progressWidth, kDefaultLineWidth);
    CGContextAddArcToPoint(context, kDefaultLineWidth, kDefaultLineWidth, kDefaultLineWidth, _radius, _radius - kDefaultLineWidth);
    CGContextAddArcToPoint(context, kDefaultLineWidth, 2.0 * _radius - kDefaultLineWidth, _radius, 2.0 * _radius - kDefaultLineWidth, _radius - kDefaultLineWidth);
    CGContextAddLineToPoint(context, progressWidth, 2.0 * _radius - kDefaultLineWidth);

    CGContextClosePath(context);
    CGContextClip(context);

    startPoint = CGPointMake(progressWidth / 2.0, kDefaultLineWidth);
    endPoint = CGPointMake(progressWidth / 2.0, 2.0 * _radius - kDefaultLineWidth);

    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);

    CGGradientRelease(gradient);
    CGColorSpaceRelease(baseSpace);

    CGContextRestoreGState(context);


    // draw the scale

    CGContextSetLineWidth(context, 0.25f);
    CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);

    float div = _progressBarWidth / 10;
    for (float i = 1; i < 10; i++) {

        CGContextMoveToPoint(context, div * i, 0.25);
        CGContextAddLineToPoint(context, div * i, _radius * 2.0 + 1.75);

        CGContextDrawPath(context, kCGPathStroke);

    }

    CGContextSetStrokeColorWithColor(context, [UIColor darkGrayColor].CGColor);

    for (float i = 1; i < 10; i++) {

        CGContextMoveToPoint(context, div * i - 0.5, 0.25);
        CGContextAddLineToPoint(context, div * i - 0.5, _radius * 2.0 + 1.75);

        CGContextDrawPath(context, kCGPathStroke);

    }

}

进度条的图像为0%&amp; 75%:

Progress bar at 0%

Progress bar at 75%

感谢您的帮助。

2 个答案:

答案 0 :(得分:0)

在动画期间,您不得多次调用绘图代码。 在您的情况下,最简单的方法是使用两个视图:一个用于开始状态,一个用于最终状态。 您可以将它们作为子视图添加到进度条视图中。 然后,您只绘制两次视图(甚至使用就绪图像)并为视图框设置动画。

E.g。

...
[self addSubview:endView];
[self addSubview:startView];
...

-(void)animateProgressFrom:(float)fromValue to:(float)toValue
{  
    CGRect startRect = self.bounds;
    startRect.size.width *= fromValue;
    startView.frame = startRect;
    [UIView animateWithDuration:duration animations:^{
        CGRect endRect = self.bounds;
        endRect.size.width *= toValue;
        startView.frame = endRect;
    }];
}

它可以是任何两个图像,而不是必要的渐变颜色。

答案 1 :(得分:0)

我最终采用了一种更简单的方法,将剪裁的UIViews&amp;使用animateWithDuration:动画:

这是主要代码 -

-(id)initWithWidth:(float)theWidth radius:(float)theRadius progress:(float)theProgress duration:(float)theDuration
{
    self = [super initWithFrame:CGRectMake(0, 0, theWidth, theRadius * 2)];
    if (self) {

        _progressBarWidth = theWidth;
        _radius = theRadius;
        _progress = (float)theProgress;
        _animationDuration = theDuration;

        self.backgroundColor = [UIColor clearColor];

        // make the view the right shape

        self.layer.cornerRadius = _radius;
        self.clipsToBounds = YES;

    }
    return self;
}


-(void)showProgress
{

    // draw the main background (which is seen as the outline)

    float tempColours[] = {0.0, 135.0 / 255.0, 237.0 / 255.0, 1.0, 1.0 / 255.0, 24.0 / 255.0, 140.0 / 255.0, 1.0};
    NSMutableArray *colours = [NSMutableArray array];
    for (int i = 0; i < 8; i++) {
        [colours addObject:[NSNumber numberWithFloat:tempColours[i]]];
    }

    // GradientView is a class which gives me UIViews with a gradient fill

    GradientView *mainBGView = [[[GradientView alloc] initWithWidth:_progressBarWidth height:2.0 * _radius colours:colours] autorelease];
    mainBGView.frame = CGRectMake(0, 0, _progressBarWidth, 2.0 * _radius);
    [self addSubview:mainBGView];


    // add the sub-background and progress bar element

    UIView *clippingContainerView = [[[UIView alloc] initWithFrame:CGRectMake(kDefaultLineWidth, kDefaultLineWidth, _progressBarWidth - 2.0 * kDefaultLineWidth, 2.0 * _radius - 2.0 * kDefaultLineWidth)] autorelease];
    clippingContainerView.backgroundColor = [UIColor clearColor];
    clippingContainerView.layer.cornerRadius = _radius - kDefaultLineWidth;
    clippingContainerView.clipsToBounds = YES;

    float tempBackgroundColours[] = {152.0 / 255.0, 152.0 / 255.0, 152.0 / 255.0, 1.0, 216.0 / 255.0, 216.0 / 255.0, 216.0 / 255.0, 1.0};
    NSMutableArray *backgroundColours = [NSMutableArray array];
    for (int i = 0; i < 8; i++) {
        [backgroundColours addObject:[NSNumber numberWithFloat:tempBackgroundColours[i]]];
    }

    GradientView *greyBGView = [[[GradientView alloc] initWithWidth:_progressBarWidth - 2.0 * kDefaultLineWidth height:2.0 * _radius - 2.0 * kDefaultLineWidth colours:backgroundColours] autorelease];
    greyBGView.frame = CGRectMake(0, 0, _progressBarWidth - 2.0 * kDefaultLineWidth, 2.0 * _radius - 2.0 * kDefaultLineWidth);
    [clippingContainerView addSubview:greyBGView];

    float tempProgressBarColours[] = {251.0 / 255.0, 215.0 / 255.0, 17.0 / 255.0, 1.0, 244.0 / 255.0, 184.0 / 255.0, 2.0 / 255.0, 1.0};
    NSMutableArray *progressBarColours = [NSMutableArray array];
    for (int i = 0; i < 8; i++) {
        [progressBarColours addObject:[NSNumber numberWithFloat:tempProgressBarColours[i]]];
    }

    GradientView *barGradientView = [[[GradientView alloc] initWithWidth:1.0 height:2.0 * _radius - 2.0 * kDefaultLineWidth colours:progressBarColours] autorelease];
    barGradientView.frame = CGRectMake(0, 0, 1, 2.0 * _radius); // start with progress bar too narrow to see
    [clippingContainerView addSubview:barGradientView];

    [self addSubview:clippingContainerView];


    // animate the progress bar

    float progressWidth = _progress * _progressBarWidth; // how wide it is at end of animation
    [UIView animateWithDuration:_animationDuration
                     animations:^{
                         barGradientView.frame = CGRectMake(0, 0, progressWidth, 2.0 * _radius);
                     }];

}

非常直接,&amp;做得很好。