0

I am trying to create my own custom UIProgressView by subclassing it and then overwrite the drawRect function. Everything works as expected except the progress filling bar. I can't get the height and image right.

The images are both in Retina resolution and the Simulator is in Retina mode. The images are called: "progressBar@2x.png" (28px high) and "progressBarTrack@2x.png" (32px high).

CustomProgressView.h

#import <UIKit/UIKit.h>

@interface CustomProgressView : UIProgressView

@end

CustomProgressView.m

#import "CustomProgressView.h"

@implementation CustomProgressView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}


// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, 16);

    UIImage *progressBarTrack = [[UIImage imageNamed:@"progressBarTrack"] resizableImageWithCapInsets:UIEdgeInsetsZero];
    UIImage *progressBar = [[UIImage imageNamed:@"progressBar"] resizableImageWithCapInsets:UIEdgeInsetsMake(4, 4, 5, 4)];

    [progressBarTrack drawInRect:rect];

    NSInteger maximumWidth = rect.size.width - 2;
    NSInteger currentWidth = floor([self progress] * maximumWidth);

    CGRect fillRect = CGRectMake(rect.origin.x + 1, rect.origin.y + 1, currentWidth, 14);

    [progressBar drawInRect:fillRect];
}

@end

The resulting ProgressView has the right height and width. It also fills at the right percentage (currently set at 80%). But the progress fill image isn't drawn correctly.

Does anyone see where I go wrong?

Screenshot to show the problem

Andrew Madsen
  • 21,309
  • 5
  • 56
  • 97
Saeverix
  • 397
  • 5
  • 18

1 Answers1

2

Looks like you're reassigning self.frame in -drawRect.

I think you want something like this:

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
    CGRect bounds = self.bounds ;

    UIImage *progressBarTrack = [ UIImage imageNamed:@"progressBarTrack"] ;
    [ progressBarTrack drawInRect:bounds ] ;

    UIImage *progressBar = [[UIImage imageNamed:@"progressBar"] resizableImageWithCapInsets:(const UIEdgeInsets){ 4.0f, 4.0f, 5.0f, 4.0f } ] ;

    CGRect fillRect = CGRectInset( bounds, 2.0f, 2.0f ) ;
    fillRect.width = floorf( self.progress * maximumWidth );

    [progressBar drawInRect:fillRect];
}

How to create your own progress view overriding UIView instead of UIProgressView

@interface ProgressView : UIView
@property float progress ;
@end

@implementation ProgressView
@synthesize progress = _progress ;

-(id)initWithFrame:(CGRect)frame
{
    if (( self = [ super initWithFrame:frame ] ))
    {
        self.layer.needsDisplayOnBoundsChange = YES ;
    }

    return self ;
}

-(void)drawRect
{
    // see code above
}

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

@end
nielsbot
  • 15,922
  • 4
  • 48
  • 73
  • I did that to make the ProgressView 16 points high instead of the default 9 points. I can't find that back in your example. – Saeverix Jul 10 '12 at 15:23
  • oh--right. you shouldn't do that here though. I would set `clipsToBounds` to `NO`, or just subclass `UIView`, not `UIProgressView`. – nielsbot Jul 10 '12 at 15:24
  • i.e. you're fighting the built-in implementation of `UIProgresssView`. In cases like these, I just make my own. Furthermore, you might run into trouble on a future iOS if they change something about their implementation etc etc. – nielsbot Jul 10 '12 at 15:25
  • Subclass UIView? Isn't that something very general? I just want to change the progressview. Or maybe I do not understand you. Can you show me an example? – Saeverix Jul 10 '12 at 15:27
  • I posted an example. It's simple! – nielsbot Jul 10 '12 at 15:33
  • Thanks, but I can't get it to work. You talk about subclassing UIView, but the interface still says UIProgressView. Changed it to UIView, but then I have no clue on how to use it. I am still a beginner, so I might not understand things right away. – Saeverix Jul 10 '12 at 17:25
  • You are right, it should have been UIView, not UIProgressView. You can use it just like progress view: alloc, initWithFrame, then call view.progress = some number or `-setProgress` on it. This isn't a final solution though, it's just example code. – nielsbot Jul 10 '12 at 17:58