Skinning a UIProgressView with drawRect and images
Here is an easy way that I’ve come up with to skin the default UIProgressView in an iOS application using just 2 images, and implementing the drawRect: method.
This is what the default progress view looks like:
![]()
…and here is our custom skinned version.
![]()
To achieve this you will need 2 images: one for the background, and one for the fill. The images that I’ve shown here were designed by Darran Morris so please give credit if you use them.
Update: You can download the images I’ve used here: progress-bar-bg.png, progress-bar-fill.png
![]()
Once you have your images created like mine above, create a new Objective-C class. In your header file make it a subclass of UIProgressView.
#import <UIKit/UIKit.h>
@interface CustomProgressView : UIProgressView {
}
@end
Next in your implementation file we will create and use our images to draw the progress bar. Note that the images have to be “stretchable” in order to work correctly and to be able to render at different widths and heights.
#import "CustomProgressView.h"
#define kCustomProgressViewFillOffsetX 1
#define kCustomProgressViewFillOffsetTopY 1
#define kCustomProgressViewFillOffsetBottomY 3
@implementation CustomProgressView
- (void)drawRect:(CGRect)rect {
CGSize backgroundStretchPoints = {4, 9}, fillStretchPoints = {3, 8};
// Initialize the stretchable images.
UIImage *background = [[UIImage imageNamed:@"progress-bar-bg.png"] stretchableImageWithLeftCapWidth:backgroundStretchPoints.width
topCapHeight:backgroundStretchPoints.height];
UIImage *fill = [[UIImage imageNamed:@"progress-bar-fill.png"] stretchableImageWithLeftCapWidth:fillStretchPoints.width
topCapHeight:fillStretchPoints.height];
// Draw the background in the current rect
[background drawInRect:rect];
// Compute the max width in pixels for the fill. Max width being how
// wide the fill should be at 100% progress.
NSInteger maxWidth = rect.size.width - (2 * kCustomProgressViewFillOffsetX);
// Compute the width for the current progress value, 0.0 - 1.0 corresponding
// to 0% and 100% respectively.
NSInteger curWidth = floor([self progress] * maxWidth);
// Create the rectangle for our fill image accounting for the position offsets,
// 1 in the X direction and 1, 3 on the top and bottom for the Y.
CGRect fillRect = CGRectMake(rect.origin.x + kCustomProgressViewFillOffsetX,
rect.origin.y + kCustomProgressViewFillOffsetTopY,
curWidth,
rect.size.height - kCustomProgressViewFillOffsetBottomY);
// Draw the fill
[fill drawInRect:fillRect];
}
@end
That’s it! Now you can use this progress view like you would any other UIView by calling initWithFrame: and passing a CGRect – and since the images are stretchable you can make it any size you want.
Thanks, a ton! I spent a lot of time looking for this solution like this.
In my console, an error appears several times due to this custom progress view. “CGImageCreateWithImageProvider: invalid image size: 5 x 5″ (The image size varies slightly throughout the errors)
Although this error doesn’t affect anything, I am still looking for a solution for this because it bugs me.
Are you familiar with this?
@Peter
I have seen errors like this before in another project (not with this code). And just like you said I never noticed it effecting anything either. I don’t recall what the solution was though, sorry…
This looks great, I can’t wait to try this out!
The problem is fillStretchPoints has a height that is too tall, that is why you see those errors.
Can you upload the source for this please?
Which source do you need? The header and implementation files are inline above.
I think Alex is referring to the pngs you used; I know that I, for one, would love to get the original copies. You know, just sayin’.
mainly just the image files for the progress bar, I’m not too savvy with photoshop.
@Alex, @Robert
I’ve updated the post to include the PNG’s.
Thanks for the example, simple clear and useful to begin to understand how to create custom progressview.
Nice work! Clean and easy to use, thanks!